1 /* base64x-1.1.16 (c) 2012-2020 Kenji Urushima | kjur.github.com/jsrsasign/license
  2  */
  3 /*
  4  * base64x.js - Base64url and supplementary functions for Tom Wu's base64.js library
  5  *
  6  * Copyright (c) 2012-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 base64x-1.1.js
 18  * @author Kenji Urushima kenji.urushima@gmail.com
 19  * @version jsrsasign 9.0.0 base64x 1.1.16 (2020-Aug-13)
 20  * @since jsrsasign 2.1
 21  * @license <a href="https://kjur.github.io/jsrsasign/license/">MIT License</a>
 22  */
 23 
 24 var KJUR;
 25 if (typeof KJUR == "undefined" || !KJUR) KJUR = {};
 26 if (typeof KJUR.lang == "undefined" || !KJUR.lang) KJUR.lang = {};
 27 
 28 /**
 29  * String and its utility class <br/>
 30  * This class provides some static utility methods for string.
 31  * @class String and its utility class
 32  * @author Kenji Urushima
 33  * @version 1.0 (2016-Aug-05)
 34  * @since base64x 1.1.7 jsrsasign 5.0.13
 35  * @description
 36  * <br/>
 37  * This class provides static methods for string utility.
 38  * <dl>
 39  * <dt><b>STRING TYPE CHECKERS</b>
 40  * <dd>
 41  * <ul>
 42  * <li>{@link KJUR.lang.String.isInteger} - check whether argument is an integer</li>
 43  * <li>{@link KJUR.lang.String.isHex} - check whether argument is a hexadecimal string</li>
 44  * <li>{@link KJUR.lang.String.isBase64} - check whether argument is a Base64 encoded string</li>
 45  * <li>{@link KJUR.lang.String.isBase64URL} - check whether argument is a Base64URL encoded string</li>
 46  * <li>{@link KJUR.lang.String.isIntegerArray} - check whether argument is an array of integers</li>
 47  * <li>{@link KJUR.lang.String.isPrintable} - check whether argument is PrintableString accepted characters</li>
 48  * <li>{@link KJUR.lang.String.isIA5} - check whether argument is IA5String accepted characters</li>
 49  * <li>{@link KJUR.lang.String.isMail} - check whether argument is RFC 822 e-mail address format</li>
 50  * </ul>
 51  * </dl>
 52  */
 53 KJUR.lang.String = function() {};
 54 
 55 /**
 56  * Base64URL and supplementary functions for Tom Wu's base64.js library.<br/>
 57  * This class is just provide information about global functions
 58  * defined in 'base64x.js'. The 'base64x.js' script file provides
 59  * global functions for converting following data each other.
 60  * <ul>
 61  * <li>(ASCII) String</li>
 62  * <li>UTF8 String including CJK, Latin and other characters</li>
 63  * <li>byte array</li>
 64  * <li>hexadecimal encoded String</li>
 65  * <li>Full URIComponent encoded String (such like "%69%94")</li>
 66  * <li>Base64 encoded String</li>
 67  * <li>Base64URL encoded String</li>
 68  * </ul>
 69  * All functions in 'base64x.js' are defined in {@link global__} and not
 70  * in this class.
 71  * 
 72  * @class Base64URL and supplementary functions for Tom Wu's base64.js library
 73  * @author Kenji Urushima
 74  * @version 1.1 (07 May 2012)
 75  * @requires base64.js
 76  * @see <a href="https://kjur.github.io/jsjws/">'jwjws'(JWS JavaScript Library) home page https://kjur.github.io/jsjws/</a>
 77  * @see <a href="https://kjur.github.io/jsrsasigns/">'jwrsasign'(RSA Sign JavaScript Library) home page https://kjur.github.io/jsrsasign/</a>
 78  */
 79 function Base64x() {
 80 }
 81 
 82 // ==== string / byte array ================================
 83 /**
 84  * convert a string to an array of character codes
 85  * @name stoBA
 86  * @function
 87  * @param {String} s
 88  * @return {Array of Numbers} 
 89  */
 90 function stoBA(s) {
 91     var a = new Array();
 92     for (var i = 0; i < s.length; i++) {
 93 	a[i] = s.charCodeAt(i);
 94     }
 95     return a;
 96 }
 97 
 98 /**
 99  * convert an array of character codes to a string
100  * @name BAtos
101  * @function
102  * @param {Array of Numbers} a array of character codes
103  * @return {String} s
104  */
105 function BAtos(a) {
106     var s = "";
107     for (var i = 0; i < a.length; i++) {
108 	s = s + String.fromCharCode(a[i]);
109     }
110     return s;
111 }
112 
113 // ==== byte array / hex ================================
114 /**
115  * convert an array of bytes(Number) to hexadecimal string.<br/>
116  * @name BAtohex
117  * @function
118  * @param {Array of Numbers} a array of bytes
119  * @return {String} hexadecimal string
120  */
121 function BAtohex(a) {
122     var s = "";
123     for (var i = 0; i < a.length; i++) {
124 	var hex1 = a[i].toString(16);
125 	if (hex1.length == 1) hex1 = "0" + hex1;
126 	s = s + hex1;
127     }
128     return s;
129 }
130 
131 // ==== string / hex ================================
132 /**
133  * convert a ASCII string to a hexadecimal string of ASCII codes.<br/>
134  * NOTE: This can't be used for non ASCII characters.
135  * @name stohex
136  * @function
137  * @param {s} s ASCII string
138  * @return {String} hexadecimal string
139  */
140 function stohex(s) {
141     return BAtohex(stoBA(s));
142 }
143 
144 // ==== string / base64 ================================
145 /**
146  * convert a ASCII string to a Base64 encoded string.<br/>
147  * NOTE: This can't be used for non ASCII characters.
148  * @name stob64
149  * @function
150  * @param {s} s ASCII string
151  * @return {String} Base64 encoded string
152  */
153 function stob64(s) {
154     return hex2b64(stohex(s));
155 }
156 
157 // ==== string / base64url ================================
158 /**
159  * convert a ASCII string to a Base64URL encoded string.<br/>
160  * NOTE: This can't be used for non ASCII characters.
161  * @name stob64u
162  * @function
163  * @param {s} s ASCII string
164  * @return {String} Base64URL encoded string
165  */
166 function stob64u(s) {
167     return b64tob64u(hex2b64(stohex(s)));
168 }
169 
170 /**
171  * convert a Base64URL encoded string to a ASCII string.<br/>
172  * NOTE: This can't be used for Base64URL encoded non ASCII characters.
173  * @name b64utos
174  * @function
175  * @param {s} s Base64URL encoded string
176  * @return {String} ASCII string
177  */
178 function b64utos(s) {
179     return BAtos(b64toBA(b64utob64(s)));
180 }
181 
182 // ==== base64 / base64url ================================
183 /**
184  * convert a Base64 encoded string to a Base64URL encoded string.<br/>
185  * @name b64tob64u
186  * @function
187  * @param {String} s Base64 encoded string
188  * @return {String} Base64URL encoded string
189  * @example
190  * b64tob64u("ab+c3f/==") → "ab-c3f_"
191  */
192 function b64tob64u(s) {
193     s = s.replace(/\=/g, "");
194     s = s.replace(/\+/g, "-");
195     s = s.replace(/\//g, "_");
196     return s;
197 }
198 
199 /**
200  * convert a Base64URL encoded string to a Base64 encoded string.<br/>
201  * @name b64utob64
202  * @function
203  * @param {String} s Base64URL encoded string
204  * @return {String} Base64 encoded string
205  * @example
206  * b64utob64("ab-c3f_") → "ab+c3f/=="
207  */
208 function b64utob64(s) {
209     if (s.length % 4 == 2) s = s + "==";
210     else if (s.length % 4 == 3) s = s + "=";
211     s = s.replace(/-/g, "+");
212     s = s.replace(/_/g, "/");
213     return s;
214 }
215 
216 // ==== hex / base64url ================================
217 /**
218  * convert a hexadecimal string to a Base64URL encoded string.<br/>
219  * @name hextob64u
220  * @function
221  * @param {String} s hexadecimal string
222  * @return {String} Base64URL encoded string
223  * @description
224  * convert a hexadecimal string to a Base64URL encoded string.
225  * NOTE: If leading "0" is omitted and odd number length for
226  * hexadecimal leading "0" is automatically added.
227  */
228 function hextob64u(s) {
229     if (s.length % 2 == 1) s = "0" + s;
230     return b64tob64u(hex2b64(s));
231 }
232 
233 /**
234  * convert a Base64URL encoded string to a hexadecimal string.<br/>
235  * @name b64utohex
236  * @function
237  * @param {String} s Base64URL encoded string
238  * @return {String} hexadecimal string
239  */
240 function b64utohex(s) {
241     return b64tohex(b64utob64(s));
242 }
243 
244 // ==== utf8 / base64url ================================
245 
246 /**
247  * convert a UTF-8 encoded string including CJK or Latin to a Base64URL encoded string.<br/>
248  * @name utf8tob64u
249  * @function
250  * @param {String} s UTF-8 encoded string
251  * @return {String} Base64URL encoded string
252  * @since 1.1
253  */
254 
255 /**
256  * convert a Base64URL encoded string to a UTF-8 encoded string including CJK or Latin.<br/>
257  * @name b64utoutf8
258  * @function
259  * @param {String} s Base64URL encoded string
260  * @return {String} UTF-8 encoded string
261  * @since 1.1
262  */
263 
264 var utf8tob64u, b64utoutf8;
265 
266 if (typeof Buffer === 'function') {
267   utf8tob64u = function (s) {
268     return b64tob64u(new Buffer(s, 'utf8').toString('base64'));
269   };
270 
271   b64utoutf8 = function (s) {
272     return new Buffer(b64utob64(s), 'base64').toString('utf8');
273   };
274 } else {
275   utf8tob64u = function (s) {
276     return hextob64u(uricmptohex(encodeURIComponentAll(s)));
277   };
278 
279   b64utoutf8 = function (s) {
280     return decodeURIComponent(hextouricmp(b64utohex(s)));
281   };
282 }
283 
284 // ==== utf8 / base64url ================================
285 /**
286  * convert a UTF-8 encoded string including CJK or Latin to a Base64 encoded string.<br/>
287  * @name utf8tob64
288  * @function
289  * @param {String} s UTF-8 encoded string
290  * @return {String} Base64 encoded string
291  * @since 1.1.1
292  */
293 function utf8tob64(s) {
294   return hex2b64(uricmptohex(encodeURIComponentAll(s)));
295 }
296 
297 /**
298  * convert a Base64 encoded string to a UTF-8 encoded string including CJK or Latin.<br/>
299  * @name b64toutf8
300  * @function
301  * @param {String} s Base64 encoded string
302  * @return {String} UTF-8 encoded string
303  * @since 1.1.1
304  */
305 function b64toutf8(s) {
306   return decodeURIComponent(hextouricmp(b64tohex(s)));
307 }
308 
309 // ==== utf8 / hex ================================
310 /**
311  * convert a UTF-8 encoded string including CJK or Latin to a hexadecimal encoded string.<br/>
312  * @name utf8tohex
313  * @function
314  * @param {String} s UTF-8 encoded string
315  * @return {String} hexadecimal encoded string
316  * @since 1.1.1
317  */
318 function utf8tohex(s) {
319   return uricmptohex(encodeURIComponentAll(s));
320 }
321 
322 /**
323  * convert a hexadecimal encoded string to a UTF-8 encoded string including CJK or Latin.<br/>
324  * Note that when input is improper hexadecimal string as UTF-8 string, this function returns
325  * 'null'.
326  * @name hextoutf8
327  * @function
328  * @param {String} s hexadecimal encoded string
329  * @return {String} UTF-8 encoded string or null
330  * @since 1.1.1
331  */
332 function hextoutf8(s) {
333   return decodeURIComponent(hextouricmp(s));
334 }
335 
336 /**
337  * convert a hexadecimal encoded string to raw string including non printable characters.<br/>
338  * @name hextorstr
339  * @function
340  * @param {String} s hexadecimal encoded string
341  * @return {String} raw string
342  * @since 1.1.2
343  * @example
344  * hextorstr("610061") → "a\x00a"
345  */
346 function hextorstr(sHex) {
347     var s = "";
348     for (var i = 0; i < sHex.length - 1; i += 2) {
349         s += String.fromCharCode(parseInt(sHex.substr(i, 2), 16));
350     }
351     return s;
352 }
353 
354 /**
355  * convert a raw string including non printable characters to hexadecimal encoded string.<br/>
356  * @name rstrtohex
357  * @function
358  * @param {String} s raw string
359  * @return {String} hexadecimal encoded string
360  * @since 1.1.2
361  * @example
362  * rstrtohex("a\x00a") → "610061"
363  */
364 function rstrtohex(s) {
365     var result = "";
366     for (var i = 0; i < s.length; i++) {
367         result += ("0" + s.charCodeAt(i).toString(16)).slice(-2);
368     }
369     return result;
370 }
371 
372 // ==== hex / b64nl =======================================
373 
374 /**
375  * convert a hexadecimal string to Base64 encoded string<br/>
376  * @name hextob64
377  * @function
378  * @param {String} s hexadecimal string
379  * @return {String} resulted Base64 encoded string
380  * @since base64x 1.1.3
381  * @description
382  * This function converts from a hexadecimal string to Base64 encoded
383  * string without new lines.
384  * @example
385  * hextob64("616161") → "YWFh"
386  */
387 function hextob64(s) {
388     return hex2b64(s);
389 }
390 
391 /**
392  * convert a hexadecimal string to Base64 encoded string with new lines<br/>
393  * @name hextob64nl
394  * @function
395  * @param {String} s hexadecimal string
396  * @return {String} resulted Base64 encoded string with new lines
397  * @since base64x 1.1.3
398  * @description
399  * This function converts from a hexadecimal string to Base64 encoded
400  * string with new lines for each 64 characters. This is useful for
401  * PEM encoded file.
402  * @example
403  * hextob64nl("123456789012345678901234567890123456789012345678901234567890")
404  * →
405  * MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4 // new line
406  * OTAxMjM0NTY3ODkwCg==
407  */
408 function hextob64nl(s) {
409     var b64 = hextob64(s);
410     var b64nl = b64.replace(/(.{64})/g, "$1\r\n");
411     b64nl = b64nl.replace(/\r\n$/, '');
412     return b64nl;
413 }
414 
415 /**
416  * convert a Base64 encoded string with new lines to a hexadecimal string<br/>
417  * @name b64nltohex
418  * @function
419  * @param {String} s Base64 encoded string with new lines
420  * @return {String} hexadecimal string
421  * @since base64x 1.1.3
422  * @description
423  * This function converts from a Base64 encoded
424  * string with new lines to a hexadecimal string.
425  * This is useful to handle PEM encoded file.
426  * This function removes any non-Base64 characters (i.e. not 0-9,A-Z,a-z,\,+,=)
427  * including new line.
428  * @example
429  * hextob64nl(
430  * "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4\r\n" +
431  * "OTAxMjM0NTY3ODkwCg==\r\n")
432  * →
433  * "123456789012345678901234567890123456789012345678901234567890"
434  */
435 function b64nltohex(s) {
436     var b64 = s.replace(/[^0-9A-Za-z\/+=]*/g, '');
437     var hex = b64tohex(b64);
438     return hex;
439 } 
440 
441 // ==== hex / pem =========================================
442 
443 /**
444  * get PEM string from hexadecimal data and header string
445  * @name hextopem
446  * @function
447  * @param {String} dataHex hexadecimal string of PEM body
448  * @param {String} pemHeader PEM header string (ex. 'RSA PRIVATE KEY')
449  * @return {String} PEM formatted string of input data
450  * @since jsrasign 7.2.1 base64x 1.1.12
451  * @description
452  * This function converts a hexadecimal string to a PEM string with
453  * a specified header. Its line break will be CRLF("\r\n").
454  * @example
455  * hextopem('616161', 'RSA PRIVATE KEY') →
456  * -----BEGIN PRIVATE KEY-----
457  * YWFh
458  * -----END PRIVATE KEY-----
459  */
460 function hextopem(dataHex, pemHeader) {
461     var pemBody = hextob64nl(dataHex);
462     return "-----BEGIN " + pemHeader + "-----\r\n" + 
463         pemBody + 
464         "\r\n-----END " + pemHeader + "-----\r\n";
465 }
466 
467 /**
468  * get hexacedimal string from PEM format data<br/>
469  * @name pemtohex
470  * @function
471  * @param {String} s PEM formatted string
472  * @param {String} sHead PEM header string without BEGIN/END(OPTION)
473  * @return {String} hexadecimal string data of PEM contents
474  * @since jsrsasign 7.2.1 base64x 1.1.12
475  * @description
476  * This static method gets a hexacedimal string of contents 
477  * from PEM format data. You can explicitly specify PEM header 
478  * by sHead argument. 
479  * Any space characters such as white space or new line
480  * will be omitted.<br/>
481  * NOTE: Now {@link KEYUTIL.getHexFromPEM} and {@link X509.pemToHex}
482  * have been deprecated since jsrsasign 7.2.1. 
483  * Please use this method instead.
484  * NOTE2: From jsrsasign 8.0.14 this can process multi
485  * "BEGIN...END" section such as "EC PRIVATE KEY" with "EC PARAMETERS".
486  * @example
487  * pemtohex("-----BEGIN PUBLIC KEY...") → "3082..."
488  * pemtohex("-----BEGIN CERTIFICATE...", "CERTIFICATE") → "3082..."
489  * pemtohex(" \r\n-----BEGIN DSA PRIVATE KEY...") → "3082..."
490  * pemtohex("-----BEGIN EC PARAMETERS...----BEGIN EC PRIVATE KEY...." → "3082..."
491  */
492 function pemtohex(s, sHead) {
493     if (s.indexOf("-----BEGIN ") == -1)
494         throw "can't find PEM header: " + sHead;
495 
496     if (sHead !== undefined) {
497         s = s.replace(new RegExp('^[^]*-----BEGIN ' + sHead + '-----'), '');
498         s = s.replace(new RegExp('-----END ' + sHead + '-----[^]*$'), '');
499     } else {
500         s = s.replace(/^[^]*-----BEGIN [^-]+-----/, '');
501         s = s.replace(/-----END [^-]+-----[^]*$/, '');
502     }
503     return b64nltohex(s);
504 }
505 
506 // ==== hex / ArrayBuffer =================================
507 
508 /**
509  * convert a hexadecimal string to an ArrayBuffer<br/>
510  * @name hextoArrayBuffer
511  * @function
512  * @param {String} hex hexadecimal string
513  * @return {ArrayBuffer} ArrayBuffer
514  * @since jsrsasign 6.1.4 base64x 1.1.8
515  * @description
516  * This function converts from a hexadecimal string to an ArrayBuffer.
517  * @example
518  * hextoArrayBuffer("fffa01") → ArrayBuffer of [255, 250, 1]
519  */
520 function hextoArrayBuffer(hex) {
521     if (hex.length % 2 != 0) throw "input is not even length";
522     if (hex.match(/^[0-9A-Fa-f]+$/) == null) throw "input is not hexadecimal";
523 
524     var buffer = new ArrayBuffer(hex.length / 2);
525     var view = new DataView(buffer);
526 
527     for (var i = 0; i < hex.length / 2; i++) {
528 	view.setUint8(i, parseInt(hex.substr(i * 2, 2), 16));
529     }
530 
531     return buffer;
532 }
533 
534 // ==== ArrayBuffer / hex =================================
535 
536 /**
537  * convert an ArrayBuffer to a hexadecimal string<br/>
538  * @name ArrayBuffertohex
539  * @function
540  * @param {ArrayBuffer} buffer ArrayBuffer
541  * @return {String} hexadecimal string
542  * @since jsrsasign 6.1.4 base64x 1.1.8
543  * @description
544  * This function converts from an ArrayBuffer to a hexadecimal string.
545  * @example
546  * var buffer = new ArrayBuffer(3);
547  * var view = new DataView(buffer);
548  * view.setUint8(0, 0xfa);
549  * view.setUint8(1, 0xfb);
550  * view.setUint8(2, 0x01);
551  * ArrayBuffertohex(buffer) → "fafb01"
552  */
553 function ArrayBuffertohex(buffer) {
554     var hex = "";
555     var view = new DataView(buffer);
556 
557     for (var i = 0; i < buffer.byteLength; i++) {
558 	hex += ("00" + view.getUint8(i).toString(16)).slice(-2);
559     }
560 
561     return hex;
562 }
563 
564 // ==== zulu / int =================================
565 /**
566  * GeneralizedTime or UTCTime string to milliseconds from Unix origin<br>
567  * @name zulutomsec
568  * @function
569  * @param {String} s GeneralizedTime or UTCTime string (ex. 20170412235959.384Z)
570  * @return {Number} milliseconds from Unix origin time (i.e. Jan 1, 1970 0:00:00 UTC)
571  * @since jsrsasign 7.1.3 base64x 1.1.9
572  * @description
573  * This function converts from GeneralizedTime string (i.e. YYYYMMDDHHmmSSZ) or
574  * UTCTime string (i.e. YYMMDDHHmmSSZ) to milliseconds from Unix origin time
575  * (i.e. Jan 1 1970 0:00:00 UTC). 
576  * Argument string may have fraction of seconds and
577  * its length is one or more digits such as "20170410235959.1234567Z".
578  * As for UTCTime, if year "YY" is equal or less than 49 then it is 20YY.
579  * If year "YY" is equal or greater than 50 then it is 19YY.
580  * @example
581  * zulutomsec(  "071231235959Z")       → 1199145599000 #Mon, 31 Dec 2007 23:59:59 GMT
582  * zulutomsec(  "071231235959.1Z")     → 1199145599100 #Mon, 31 Dec 2007 23:59:59 GMT
583  * zulutomsec(  "071231235959.12345Z") → 1199145599123 #Mon, 31 Dec 2007 23:59:59 GMT
584  * zulutomsec("20071231235959Z")       → 1199145599000 #Mon, 31 Dec 2007 23:59:59 GMT
585  * zulutomsec(  "931231235959Z")       → -410227201000 #Mon, 31 Dec 1956 23:59:59 GMT
586  */
587 function zulutomsec(s) {
588     var year, month, day, hour, min, sec, msec, d;
589     var sYear, sFrac, sMsec, matchResult;
590 
591     matchResult = s.match(/^(\d{2}|\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(|\.\d+)Z$/);
592 
593     if (matchResult) {
594         sYear = matchResult[1];
595 	year = parseInt(sYear);
596         if (sYear.length === 2) {
597 	    if (50 <= year && year < 100) {
598 		year = 1900 + year;
599 	    } else if (0 <= year && year < 50) {
600 		year = 2000 + year;
601 	    }
602 	}
603 	month = parseInt(matchResult[2]) - 1;
604 	day = parseInt(matchResult[3]);
605 	hour = parseInt(matchResult[4]);
606 	min = parseInt(matchResult[5]);
607 	sec = parseInt(matchResult[6]);
608 	msec = 0;
609 
610 	sFrac = matchResult[7];
611 	if (sFrac !== "") {
612 	    sMsec = (sFrac.substr(1) + "00").substr(0, 3); // .12 -> 012
613 	    msec = parseInt(sMsec);
614 	}
615 	return Date.UTC(year, month, day, hour, min, sec, msec);
616     }
617     throw "unsupported zulu format: " + s;
618 }
619 
620 /**
621  * GeneralizedTime or UTCTime string to seconds from Unix origin<br>
622  * @name zulutosec
623  * @function
624  * @param {String} s GeneralizedTime or UTCTime string (ex. 20170412235959.384Z)
625  * @return {Number} seconds from Unix origin time (i.e. Jan 1, 1970 0:00:00 UTC)
626  * @since jsrsasign 7.1.3 base64x 1.1.9
627  * @description
628  * This function converts from GeneralizedTime string (i.e. YYYYMMDDHHmmSSZ) or
629  * UTCTime string (i.e. YYMMDDHHmmSSZ) to seconds from Unix origin time
630  * (i.e. Jan 1 1970 0:00:00 UTC). Argument string may have fraction of seconds 
631  * however result value will be omitted.
632  * As for UTCTime, if year "YY" is equal or less than 49 then it is 20YY.
633  * If year "YY" is equal or greater than 50 then it is 19YY.
634  * @example
635  * zulutosec(  "071231235959Z")       → 1199145599 #Mon, 31 Dec 2007 23:59:59 GMT
636  * zulutosec(  "071231235959.1Z")     → 1199145599 #Mon, 31 Dec 2007 23:59:59 GMT
637  * zulutosec("20071231235959Z")       → 1199145599 #Mon, 31 Dec 2007 23:59:59 GMT
638  */
639 function zulutosec(s) {
640     var msec = zulutomsec(s);
641     return ~~(msec / 1000);
642 }
643 
644 // ==== zulu / Date =================================
645 
646 /**
647  * GeneralizedTime or UTCTime string to Date object<br>
648  * @name zulutodate
649  * @function
650  * @param {String} s GeneralizedTime or UTCTime string (ex. 20170412235959.384Z)
651  * @return {Date} Date object for specified time
652  * @since jsrsasign 7.1.3 base64x 1.1.9
653  * @description
654  * This function converts from GeneralizedTime string (i.e. YYYYMMDDHHmmSSZ) or
655  * UTCTime string (i.e. YYMMDDHHmmSSZ) to Date object.
656  * Argument string may have fraction of seconds and
657  * its length is one or more digits such as "20170410235959.1234567Z".
658  * As for UTCTime, if year "YY" is equal or less than 49 then it is 20YY.
659  * If year "YY" is equal or greater than 50 then it is 19YY.
660  * @example
661  * zulutodate(  "071231235959Z").toUTCString()   → "Mon, 31 Dec 2007 23:59:59 GMT"
662  * zulutodate(  "071231235959.1Z").toUTCString() → "Mon, 31 Dec 2007 23:59:59 GMT"
663  * zulutodate("20071231235959Z").toUTCString()   → "Mon, 31 Dec 2007 23:59:59 GMT"
664  * zulutodate(  "071231235959.34").getMilliseconds() → 340
665  */
666 function zulutodate(s) {
667     return new Date(zulutomsec(s));
668 }
669 
670 // ==== Date / zulu =================================
671 
672 /**
673  * Date object to zulu time string<br>
674  * @name datetozulu
675  * @function
676  * @param {Date} d Date object for specified time
677  * @param {Boolean} flagUTCTime if this is true year will be YY otherwise YYYY
678  * @param {Boolean} flagMilli if this is true result concludes milliseconds
679  * @return {String} GeneralizedTime or UTCTime string (ex. 20170412235959.384Z)
680  * @since jsrsasign 7.2.0 base64x 1.1.11
681  * @description
682  * This function converts from Date object to GeneralizedTime string (i.e. YYYYMMDDHHmmSSZ) or
683  * UTCTime string (i.e. YYMMDDHHmmSSZ).
684  * As for UTCTime, if year "YY" is equal or less than 49 then it is 20YY.
685  * If year "YY" is equal or greater than 50 then it is 19YY.
686  * If flagMilli is true its result concludes milliseconds such like
687  * "20170520235959.42Z". 
688  * @example
689  * d = new Date(Date.UTC(2017,4,20,23,59,59,670));
690  * datetozulu(d) → "20170520235959Z"
691  * datetozulu(d, true) → "170520235959Z"
692  * datetozulu(d, false, true) → "20170520235959.67Z"
693  */
694 function datetozulu(d, flagUTCTime, flagMilli) {
695     var s;
696     var year = d.getUTCFullYear();
697     if (flagUTCTime) {
698 	if (year < 1950 || 2049 < year) 
699 	    throw "not proper year for UTCTime: " + year;
700 	s = ("" + year).slice(-2);
701     } else {
702 	s = ("000" + year).slice(-4);
703     }
704     s += ("0" + (d.getUTCMonth() + 1)).slice(-2);
705     s += ("0" + d.getUTCDate()).slice(-2);
706     s += ("0" + d.getUTCHours()).slice(-2);
707     s += ("0" + d.getUTCMinutes()).slice(-2);
708     s += ("0" + d.getUTCSeconds()).slice(-2);
709     if (flagMilli) {
710 	var milli = d.getUTCMilliseconds();
711 	if (milli !== 0) {
712 	    milli = ("00" + milli).slice(-3);
713 	    milli = milli.replace(/0+$/g, "");
714 	    s += "." + milli;
715 	}
716     }
717     s += "Z";
718     return s;
719 }
720 
721 // ==== URIComponent / hex ================================
722 /**
723  * convert a URLComponent string such like "%67%68" to a hexadecimal string.<br/>
724  * @name uricmptohex
725  * @function
726  * @param {String} s URIComponent string such like "%67%68"
727  * @return {String} hexadecimal string
728  * @since 1.1
729  */
730 function uricmptohex(s) {
731   return s.replace(/%/g, "");
732 }
733 
734 /**
735  * convert a hexadecimal string to a URLComponent string such like "%67%68".<br/>
736  * @name hextouricmp
737  * @function
738  * @param {String} s hexadecimal string
739  * @return {String} URIComponent string such like "%67%68"
740  * @since 1.1
741  */
742 function hextouricmp(s) {
743   return s.replace(/(..)/g, "%$1");
744 }
745 
746 // ==== hex / ipv6 =================================
747 
748 /**
749  * convert any IPv6 address to a 16 byte hexadecimal string
750  * @function
751  * @param s string of IPv6 address
752  * @return {String} 16 byte hexadecimal string of IPv6 address
753  * @description
754  * This function converts any IPv6 address representation string
755  * to a 16 byte hexadecimal string of address.
756  * @example
757  * 
758  */
759 function ipv6tohex(s) {
760   var msgMalformedAddress = "malformed IPv6 address";
761   if (! s.match(/^[0-9A-Fa-f:]+$/))
762     throw msgMalformedAddress;
763 
764   // 1. downcase
765   s = s.toLowerCase();
766 
767   // 2. expand ::
768   var num_colon = s.split(':').length - 1;
769   if (num_colon < 2) throw msgMalformedAddress;
770   var colon_replacer = ':'.repeat(7 - num_colon + 2);
771   s = s.replace('::', colon_replacer);
772 
773   // 3. fill zero
774   var a = s.split(':');
775   if (a.length != 8) throw msgMalformedAddress;
776   for (var i = 0; i < 8; i++) {
777     a[i] = ("0000" + a[i]).slice(-4);
778   }
779   return a.join('');
780 }
781 
782 /**
783  * convert a 16 byte hexadecimal string to RFC 5952 canonicalized IPv6 address<br/>
784  * @name hextoipv6
785  * @function
786  * @param {String} s hexadecimal string of 16 byte IPv6 address
787  * @return {String} IPv6 address string canonicalized by RFC 5952
788  * @since jsrsasign 8.0.10 base64x 1.1.13
789  * @description
790  * This function converts a 16 byte hexadecimal string to 
791  * <a href="https://tools.ietf.org/html/rfc5952">RFC 5952</a>
792  * canonicalized IPv6 address string.
793  * @example
794  * hextoip("871020010db8000000000000000000000004") &rarr "2001:db8::4"
795  * hextoip("871020010db8000000000000000000") &rarr raise exception
796  * hextoip("xyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyz") &rarr raise exception
797  */
798 function hextoipv6(s) {
799   if (! s.match(/^[0-9A-Fa-f]{32}$/))
800     throw "malformed IPv6 address octet";
801 
802   // 1. downcase
803   s = s.toLowerCase();
804 
805   // 2. split 4
806   var a = s.match(/.{1,4}/g);
807 
808   // 3. trim leading 0
809   for (var i = 0; i < 8; i++) {
810     a[i] = a[i].replace(/^0+/, "");
811     if (a[i] == '') a[i] = '0';
812   }
813   s = ":" + a.join(":") + ":";
814 
815   // 4. find shrinkables :0:0:...
816   var aZero = s.match(/:(0:){2,}/g);
817 
818   // 5. no shrinkable
819   if (aZero === null) return s.slice(1, -1);
820 
821   // 6. find max length :0:0:...
822   var item = '';
823   for (var i = 0; i < aZero.length; i++) {
824     if (aZero[i].length > item.length) item = aZero[i];
825   }
826 
827   // 7. shrink
828   s = s.replace(item, '::');
829   return s.slice(1, -1);
830 }
831 
832 // ==== hex / ip =================================
833 
834 /**
835  * convert a hexadecimal string to IP addresss<br/>
836  * @name hextoip
837  * @function
838  * @param {String} s hexadecimal string of IP address
839  * @return {String} IP address string
840  * @since jsrsasign 8.0.10 base64x 1.1.13
841  * @description
842  * This function converts a hexadecimal string of IPv4 or 
843  * IPv6 address to IPv4 or IPv6 address string.
844  * If byte length is not 4 nor 16, this returns a
845  * hexadecimal string without conversion.
846  * @see {@link hextoipv6}
847  * @example
848  * hextoip("c0a80101") &rarr "192.168.1.1"
849  * hextoip("871020010db8000000000000000000000004") &rarr "2001:db8::4"
850  * hextoip("c0a801010203") &rarr "c0a801010203" // 6 bytes
851  * hextoip("zzz")) &rarr raise exception because of not hexadecimal
852  */
853 function hextoip(s) {
854   var malformedMsg = "malformed hex value";
855   if (! s.match(/^([0-9A-Fa-f][0-9A-Fa-f]){1,}$/))
856     throw malformedMsg;
857   if (s.length == 8) { // ipv4
858     var ip;
859     try {
860       ip = parseInt(s.substr(0, 2), 16) + "." +
861            parseInt(s.substr(2, 2), 16) + "." +
862            parseInt(s.substr(4, 2), 16) + "." +
863            parseInt(s.substr(6, 2), 16);
864       return ip;
865     } catch (ex) {
866       throw malformedMsg;
867     }
868   } else if (s.length == 32) {
869     return hextoipv6(s);
870   } else {
871     return s;
872   }
873 }
874 
875 /**
876  * convert IPv4/v6 addresss to a hexadecimal string<br/>
877  * @name iptohex
878  * @function
879  * @param {String} s IPv4/v6 address string
880  * @return {String} hexadecimal string of IP address
881  * @since jsrsasign 8.0.12 base64x 1.1.14
882  * @description
883  * This function converts IPv4 or IPv6 address string to
884  * a hexadecimal string of IPv4 or IPv6 address.
885  * @example
886  * iptohex("192.168.1.1") &rarr "c0a80101"
887  * iptohex("2001:db8::4") &rarr "871020010db8000000000000000000000004"
888  * iptohex("zzz")) &rarr raise exception
889  */
890 function iptohex(s) {
891   var malformedMsg = "malformed IP address";
892   s = s.toLowerCase(s);
893 
894   if (s.match(/^[0-9.]+$/)) {
895     var a = s.split(".");
896     if (a.length !== 4) throw malformedMsg;
897     var hex = "";
898     try {
899       for (var i = 0; i < 4; i++) {
900         var d = parseInt(a[i]);
901         hex += ("0" + d.toString(16)).slice(-2);
902       }
903       return hex;
904     } catch(ex) {
905       throw malformedMsg;
906     }
907   } else if (s.match(/^[0-9a-f:]+$/) && s.indexOf(":") !== -1) {
908     return ipv6tohex(s);
909   } else {
910     throw malformedMsg;
911   }
912 }
913 
914 // ==== URIComponent ================================
915 /**
916  * convert UTFa hexadecimal string to a URLComponent string such like "%67%68".<br/>
917  * Note that these "<code>0-9A-Za-z!'()*-._~</code>" characters will not
918  * converted to "%xx" format by builtin 'encodeURIComponent()' function.
919  * However this 'encodeURIComponentAll()' function will convert 
920  * all of characters into "%xx" format.
921  * @name encodeURIComponentAll
922  * @function
923  * @param {String} s hexadecimal string
924  * @return {String} URIComponent string such like "%67%68"
925  * @since 1.1
926  */
927 function encodeURIComponentAll(u8) {
928   var s = encodeURIComponent(u8);
929   var s2 = "";
930   for (var i = 0; i < s.length; i++) {
931     if (s[i] == "%") {
932       s2 = s2 + s.substr(i, 3);
933       i = i + 2;
934     } else {
935       s2 = s2 + "%" + stohex(s[i]);
936     }
937   }
938   return s2;
939 }
940 
941 // ==== new lines ================================
942 /**
943  * convert all DOS new line("\r\n") to UNIX new line("\n") in 
944  * a String "s".
945  * @name newline_toUnix
946  * @function
947  * @param {String} s string 
948  * @return {String} converted string
949  */
950 function newline_toUnix(s) {
951     s = s.replace(/\r\n/mg, "\n");
952     return s;
953 }
954 
955 /**
956  * convert all UNIX new line("\r\n") to DOS new line("\n") in 
957  * a String "s".
958  * @name newline_toDos
959  * @function
960  * @param {String} s string 
961  * @return {String} converted string
962  */
963 function newline_toDos(s) {
964     s = s.replace(/\r\n/mg, "\n");
965     s = s.replace(/\n/mg, "\r\n");
966     return s;
967 }
968 
969 // ==== string type checker ===================
970 
971 /**
972  * check whether a string is an integer string or not<br/>
973  * @name isInteger
974  * @memberOf KJUR.lang.String
975  * @function
976  * @static
977  * @param {String} s input string
978  * @return {Boolean} true if a string "s" is an integer string otherwise false
979  * @since base64x 1.1.7 jsrsasign 5.0.13
980  * @example
981  * KJUR.lang.String.isInteger("12345") → true
982  * KJUR.lang.String.isInteger("123ab") → false
983  */
984 KJUR.lang.String.isInteger = function(s) {
985     if (s.match(/^[0-9]+$/)) {
986 	return true;
987     } else if (s.match(/^-[0-9]+$/)) {
988 	return true;
989     } else {
990 	return false;
991     }
992 };
993 
994 /**
995  * check whether a string is an hexadecimal string or not<br/>
996  * @name isHex
997  * @memberOf KJUR.lang.String
998  * @function
999  * @static
1000  * @param {String} s input string
1001  * @return {Boolean} true if a string "s" is an hexadecimal string otherwise false
1002  * @since base64x 1.1.7 jsrsasign 5.0.13
1003  * @example
1004  * KJUR.lang.String.isHex("1234") → true
1005  * KJUR.lang.String.isHex("12ab") → true
1006  * KJUR.lang.String.isHex("12AB") → true
1007  * KJUR.lang.String.isHex("12ZY") → false
1008  * KJUR.lang.String.isHex("121") → false -- odd length
1009  */
1010 KJUR.lang.String.isHex = function(s) {
1011     if (s.length % 2 == 0 &&
1012 	(s.match(/^[0-9a-f]+$/) || s.match(/^[0-9A-F]+$/))) {
1013 	return true;
1014     } else {
1015 	return false;
1016     }
1017 };
1018 
1019 /**
1020  * check whether a string is a base64 encoded string or not<br/>
1021  * Input string can conclude new lines or space characters.
1022  * @name isBase64
1023  * @memberOf KJUR.lang.String
1024  * @function
1025  * @static
1026  * @param {String} s input string
1027  * @return {Boolean} true if a string "s" is a base64 encoded string otherwise false
1028  * @since base64x 1.1.7 jsrsasign 5.0.13
1029  * @example
1030  * KJUR.lang.String.isBase64("YWE=") → true
1031  * KJUR.lang.String.isBase64("YW_=") → false
1032  * KJUR.lang.String.isBase64("YWE") → false -- length shall be multiples of 4
1033  */
1034 KJUR.lang.String.isBase64 = function(s) {
1035     s = s.replace(/\s+/g, "");
1036     if (s.match(/^[0-9A-Za-z+\/]+={0,3}$/) && s.length % 4 == 0) {
1037 	return true;
1038     } else {
1039 	return false;
1040     }
1041 };
1042 
1043 /**
1044  * check whether a string is a base64url encoded string or not<br/>
1045  * Input string can conclude new lines or space characters.
1046  * @name isBase64URL
1047  * @memberOf KJUR.lang.String
1048  * @function
1049  * @static
1050  * @param {String} s input string
1051  * @return {Boolean} true if a string "s" is a base64url encoded string otherwise false
1052  * @since base64x 1.1.7 jsrsasign 5.0.13
1053  * @example
1054  * KJUR.lang.String.isBase64URL("YWE") → true
1055  * KJUR.lang.String.isBase64URL("YW-") → true
1056  * KJUR.lang.String.isBase64URL("YW+") → false
1057  */
1058 KJUR.lang.String.isBase64URL = function(s) {
1059     if (s.match(/[+/=]/)) return false;
1060     s = b64utob64(s);
1061     return KJUR.lang.String.isBase64(s);
1062 };
1063 
1064 /**
1065  * check whether a string is a string of integer array or not<br/>
1066  * Input string can conclude new lines or space characters.
1067  * @name isIntegerArray
1068  * @memberOf KJUR.lang.String
1069  * @function
1070  * @static
1071  * @param {String} s input string
1072  * @return {Boolean} true if a string "s" is a string of integer array otherwise false
1073  * @since base64x 1.1.7 jsrsasign 5.0.13
1074  * @example
1075  * KJUR.lang.String.isIntegerArray("[1,2,3]") → true
1076  * KJUR.lang.String.isIntegerArray("  [1, 2, 3  ] ") → true
1077  * KJUR.lang.String.isIntegerArray("[a,2]") → false
1078  */
1079 KJUR.lang.String.isIntegerArray = function(s) {
1080     s = s.replace(/\s+/g, "");
1081     if (s.match(/^\[[0-9,]+\]$/)) {
1082 	return true;
1083     } else {
1084 	return false;
1085     }
1086 };
1087 
1088 /**
1089  * check whether a string consists of PrintableString characters<br/>
1090  * @name isPrintable
1091  * @memberOf KJUR.lang.String
1092  * @function
1093  * @static
1094  * @param {String} s input string
1095  * @return {Boolean} true if a string "s" consists of PrintableString characters
1096  * @since jsrsasign 9.0.0 base64x 1.1.16
1097  * A PrintableString consists of following characters
1098  * <pre>
1099  * 0-9A-Za-z '()+,-./:=?
1100  * </pre>
1101  * This method returns false when other characters than above.
1102  * Otherwise it returns true.
1103  * @example
1104  * KJUR.lang.String.isPrintable("abc") → true
1105  * KJUR.lang.String.isPrintable("abc@") → false
1106  * KJUR.lang.String.isPrintable("あいう") → false
1107  */
1108 KJUR.lang.String.isPrintable = function(s) {
1109     if (s.match(/^[0-9A-Za-z '()+,-./:=?]*$/) !== null) return true;
1110     return false;
1111 };
1112 
1113 /**
1114  * check whether a string consists of IAString characters<br/>
1115  * @name isIA5
1116  * @memberOf KJUR.lang.String
1117  * @function
1118  * @static
1119  * @param {String} s input string
1120  * @return {Boolean} true if a string "s" consists of IA5String characters
1121  * @since jsrsasign 9.0.0 base64x 1.1.16
1122  * A IA5String consists of following characters
1123  * <pre>
1124  * %x00-21/%x23-7F (i.e. ASCII characters excludes double quote(%x22)
1125  * </pre>
1126  * This method returns false when other characters than above.
1127  * Otherwise it returns true.
1128  * @example
1129  * KJUR.lang.String.isIA5("abc") → true
1130  * KJUR.lang.String.isIA5('"abc"') → false
1131  * KJUR.lang.String.isIA5("あいう") → false
1132  */
1133 KJUR.lang.String.isIA5 = function(s) {
1134     if (s.match(/^[\x20-\x21\x23-\x7f]*$/) !== null) return true;
1135     return false;
1136 };
1137 
1138 /**
1139  * check whether a string is RFC 822 mail address<br/>
1140  * @name isMail
1141  * @memberOf KJUR.lang.String
1142  * @function
1143  * @static
1144  * @param {String} s input string
1145  * @return {Boolean} true if a string "s" RFC 822 mail address
1146  * @since jsrsasign 9.0.0 base64x 1.1.16
1147  * This static method will check string s is RFC 822 compliant mail address.
1148  * @example
1149  * KJUR.lang.String.isMail("abc") → false
1150  * KJUR.lang.String.isMail("abc@example") → false
1151  * KJUR.lang.String.isMail("abc@example.com") → true
1152  */
1153 KJUR.lang.String.isMail = function(s) {
1154     if (s.match(/^[A-Za-z0-9]{1}[A-Za-z0-9_.-]*@{1}[A-Za-z0-9_.-]{1,}\.[A-Za-z0-9]{1,}$/) !== null) return true;
1155     return false;
1156 };
1157 
1158 // ==== others ================================
1159 
1160 /**
1161  * canonicalize hexadecimal string of positive integer<br/>
1162  * @name hextoposhex
1163  * @function
1164  * @param {String} s hexadecimal string 
1165  * @return {String} canonicalized hexadecimal string of positive integer
1166  * @since base64x 1.1.10 jsrsasign 7.1.4
1167  * @description
1168  * This method canonicalize a hexadecimal string of positive integer
1169  * for two's complement representation.
1170  * Canonicalized hexadecimal string of positive integer will be:
1171  * <ul>
1172  * <li>Its length is always even.</li>
1173  * <li>If odd length it will be padded with leading zero.<li>
1174  * <li>If it is even length and its first character is "8" or greater,
1175  * it will be padded with "00" to make it positive integer.</li>
1176  * </ul>
1177  * @example
1178  * hextoposhex("abcd") → "00abcd"
1179  * hextoposhex("1234") → "1234"
1180  * hextoposhex("12345") → "012345"
1181  */
1182 function hextoposhex(s) {
1183     if (s.length % 2 == 1) return "0" + s;
1184     if (s.substr(0, 1) > "7") return "00" + s;
1185     return s;
1186 }
1187 
1188 /**
1189  * convert string of integer array to hexadecimal string.<br/>
1190  * @name intarystrtohex
1191  * @function
1192  * @param {String} s string of integer array
1193  * @return {String} hexadecimal string
1194  * @since base64x 1.1.6 jsrsasign 5.0.2
1195  * @throws "malformed integer array string: *" for wrong input
1196  * @description
1197  * This function converts a string of JavaScript integer array to
1198  * a hexadecimal string. Each integer value shall be in a range 
1199  * from 0 to 255 otherwise it raise exception. Input string can
1200  * have extra space or newline string so that they will be ignored.
1201  * 
1202  * @example
1203  * intarystrtohex(" [123, 34, 101, 34, 58] ")
1204  * → 7b2265223a (i.e. '{"e":' as string)
1205  */
1206 function intarystrtohex(s) {
1207   s = s.replace(/^\s*\[\s*/, '');
1208   s = s.replace(/\s*\]\s*$/, '');
1209   s = s.replace(/\s*/g, '');
1210   try {
1211     var hex = s.split(/,/).map(function(element, index, array) {
1212       var i = parseInt(element);
1213       if (i < 0 || 255 < i) throw "integer not in range 0-255";
1214       var hI = ("00" + i.toString(16)).slice(-2);
1215       return hI;
1216     }).join('');
1217     return hex;
1218   } catch(ex) {
1219     throw "malformed integer array string: " + ex;
1220   }
1221 }
1222 
1223 /**
1224  * find index of string where two string differs
1225  * @name strdiffidx
1226  * @function
1227  * @param {String} s1 string to compare
1228  * @param {String} s2 string to compare
1229  * @return {Number} string index of where character differs. Return -1 if same.
1230  * @since jsrsasign 4.9.0 base64x 1.1.5
1231  * @example
1232  * strdiffidx("abcdefg", "abcd4fg") -> 4
1233  * strdiffidx("abcdefg", "abcdefg") -> -1
1234  * strdiffidx("abcdefg", "abcdef") -> 6
1235  * strdiffidx("abcdefgh", "abcdef") -> 6
1236  */
1237 var strdiffidx = function(s1, s2) {
1238     var n = s1.length;
1239     if (s1.length > s2.length) n = s2.length;
1240     for (var i = 0; i < n; i++) {
1241 	if (s1.charCodeAt(i) != s2.charCodeAt(i)) return i;
1242     }
1243     if (s1.length != s2.length) return n;
1244     return -1; // same
1245 };
1246 
1247 
1248