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