typepki-strconv 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +22 -0
- package/dist/import/conv.d.mts +193 -0
- package/dist/import/conv.d.mts.map +1 -0
- package/dist/import/conv_arybuf.d.mts +30 -0
- package/dist/import/conv_arybuf.d.mts.map +1 -0
- package/dist/import/conv_ba.d.mts +27 -0
- package/dist/import/conv_ba.d.mts.map +1 -0
- package/dist/import/conv_ip.d.mts +69 -0
- package/dist/import/conv_ip.d.mts.map +1 -0
- package/dist/import/conv_iso88591.d.mts +19 -0
- package/dist/import/conv_iso88591.d.mts.map +1 -0
- package/dist/import/conv_pem.d.mts +109 -0
- package/dist/import/conv_pem.d.mts.map +1 -0
- package/dist/import/conv_ucs2.d.mts +14 -0
- package/dist/import/conv_ucs2.d.mts.map +1 -0
- package/dist/import/conv_uricmp.d.mts +29 -0
- package/dist/import/conv_uricmp.d.mts.map +1 -0
- package/dist/import/conv_zulu.d.mts +120 -0
- package/dist/import/conv_zulu.d.mts.map +1 -0
- package/dist/import/index.d.mts +22 -0
- package/dist/import/index.d.mts.map +1 -0
- package/dist/import/index.mjs +4 -0
- package/dist/require/index.cjs +4 -0
- package/dist/require/index.d.cts +2 -0
- package/dist/require/index.d.cts.map +1 -0
- package/package.json +72 -0
- package/src/conv.mts +367 -0
- package/src/conv.test.mts +99 -0
- package/src/conv_arybuf.mts +53 -0
- package/src/conv_arybuf.test.mts +16 -0
- package/src/conv_ba.mts +52 -0
- package/src/conv_ip.mts +226 -0
- package/src/conv_iso88591.mts +63 -0
- package/src/conv_pem.mts +160 -0
- package/src/conv_ucs2.mts +47 -0
- package/src/conv_uricmp.mts +48 -0
- package/src/conv_uricmp.test.mts +16 -0
- package/src/conv_zulu.mts +208 -0
- package/src/conv_zulu.test.mts +49 -0
- package/src/index.cts +1 -0
- package/src/index.mts +100 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Dictionary,
|
|
5
|
+
VERSION,
|
|
6
|
+
aryval,
|
|
7
|
+
b64tob64u,
|
|
8
|
+
b64tohex,
|
|
9
|
+
b64utob64,
|
|
10
|
+
b64utohex,
|
|
11
|
+
binstrtobitstr,
|
|
12
|
+
hextob64,
|
|
13
|
+
hextob64u,
|
|
14
|
+
hextorstr,
|
|
15
|
+
inttobitstr,
|
|
16
|
+
namearraytobinstr,
|
|
17
|
+
rstrtob64,
|
|
18
|
+
rstrtohex,
|
|
19
|
+
strpad,
|
|
20
|
+
} from "./index.mts";
|
|
21
|
+
|
|
22
|
+
// ==== VERSION ===========================================
|
|
23
|
+
test("VERSION", () => {
|
|
24
|
+
expect(VERSION.substr(0, 16)).toBe("typepki-strconv ");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// ==== hex / base64 ======================================
|
|
28
|
+
test("b64tohex", () => {
|
|
29
|
+
expect(b64tohex("YWFh")).toBe("616161");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("hextob64", () => {
|
|
33
|
+
expect(hextob64("616161")).toBe("YWFh");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// ==== hex / base64u =====================================
|
|
37
|
+
test("b64utohex", () => {
|
|
38
|
+
expect(b64utohex("YWFh")).toBe("616161");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("hextob64u", () => {
|
|
42
|
+
expect(hextob64u("616161")).toBe("YWFh");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// ==== base64 / base64u ==================================
|
|
46
|
+
|
|
47
|
+
test("b64tob64u", () => {
|
|
48
|
+
expect(b64tob64u("ab+c3f/==")).toBe("ab-c3f_");
|
|
49
|
+
expect(b64utob64("ab-c3f_")).toBe("ab+c3f/="); // ?? いいの?
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("binstrtobitstr", () => {
|
|
53
|
+
expect(binstrtobitstr("101")).toBe("05a0");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("inttobitstr", () => {
|
|
57
|
+
expect(inttobitstr(0b11001)).toBe("03c8");
|
|
58
|
+
expect(inttobitstr(0b101)).toBe("05a0");
|
|
59
|
+
expect(inttobitstr(0b101000001)).toBe("07a080");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("strpad", () => {
|
|
63
|
+
expect(strpad("1234", 10, "0")).toBe("0000001234");
|
|
64
|
+
expect(strpad("1234", 10, " ")).toBe(" 1234");
|
|
65
|
+
expect(strpad("1234", 10)).toBe("0000001234");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("namearraytobinstr", () => {
|
|
69
|
+
const db: Dictionary<number> = { a: 0, b: 3, c: 8, d: 9, e: 17, f: 19 };
|
|
70
|
+
expect(namearraytobinstr(["a", "c", "d"], db)).toBe("1000000011");
|
|
71
|
+
expect(namearraytobinstr(["c", "b"], db)).toBe("000100001");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("aryval", () => {
|
|
75
|
+
const p = {
|
|
76
|
+
fruit: "apple",
|
|
77
|
+
info: [{ toy: 4 }, { pen: 6 }],
|
|
78
|
+
};
|
|
79
|
+
expect(aryval(p, "fruit")).toBe("apple");
|
|
80
|
+
expect(aryval(p, "info")).toEqual([{ toy: 4 }, { pen: 6 }]);
|
|
81
|
+
expect(aryval(p, "info.1")).toEqual({ pen: 6 });
|
|
82
|
+
expect(aryval(p, "info.1.pen")).toBe(6);
|
|
83
|
+
expect(aryval(p, "money.amount")).toBe(undefined);
|
|
84
|
+
expect(aryval(p, "money.amount", null)).toBe(null);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("hextorstr", () => {
|
|
88
|
+
expect(hextorstr("616161")).toBe("aaa");
|
|
89
|
+
expect(hextorstr("000000")).toBe("\x00\x00\x00");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("rstrtohex", () => {
|
|
93
|
+
expect(rstrtohex("aaa")).toBe("616161");
|
|
94
|
+
expect(rstrtohex("\x00\x00\x00")).toBe("000000");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("rstrtob64", () => {
|
|
98
|
+
expect(rstrtob64("aaa")).toBe("YWFh");
|
|
99
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* convert a hexadecimal string to an ArrayBuffer
|
|
3
|
+
* @param hex - hexadecimal string
|
|
4
|
+
* @return ArrayBuffer
|
|
5
|
+
*
|
|
6
|
+
* @description
|
|
7
|
+
* This function converts from a hexadecimal string to an ArrayBuffer.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* hextoArrayBuffer("fffa01") -> ArrayBuffer of [255, 250, 1]
|
|
11
|
+
*/
|
|
12
|
+
export function hextoArrayBuffer(hex: string): ArrayBuffer {
|
|
13
|
+
if (hex.length % 2 !== 0) throw "input is not even length";
|
|
14
|
+
if (hex.match(/^[0-9A-Fa-f]+$/) == null) throw "input is not hexadecimal";
|
|
15
|
+
|
|
16
|
+
const buffer = new ArrayBuffer(hex.length / 2);
|
|
17
|
+
const view = new DataView(buffer);
|
|
18
|
+
|
|
19
|
+
for (let i = 0; i < hex.length / 2; i++) {
|
|
20
|
+
view.setUint8(i, parseInt(hex.substr(i * 2, 2), 16));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return buffer;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ==== ArrayBuffer / hex =================================
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* convert an ArrayBuffer to a hexadecimal string
|
|
30
|
+
* @param buffer - ArrayBuffer
|
|
31
|
+
* @return hexadecimal string
|
|
32
|
+
*
|
|
33
|
+
* @description
|
|
34
|
+
* This function converts from an ArrayBuffer to a hexadecimal string.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* var buffer = new ArrayBuffer(3);
|
|
38
|
+
* var view = new DataView(buffer);
|
|
39
|
+
* view.setUint8(0, 0xfa);
|
|
40
|
+
* view.setUint8(1, 0xfb);
|
|
41
|
+
* view.setUint8(2, 0x01);
|
|
42
|
+
* ArrayBuffertohex(buffer) -> "fafb01"
|
|
43
|
+
*/
|
|
44
|
+
export function ArrayBuffertohex(buffer: ArrayBuffer): string {
|
|
45
|
+
let hex = "";
|
|
46
|
+
const view = new DataView(buffer);
|
|
47
|
+
|
|
48
|
+
for (let i = 0; i < buffer.byteLength; i++) {
|
|
49
|
+
hex += `00${view.getUint8(i).toString(16)}`.slice(-2);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return hex;
|
|
53
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { ArrayBuffertohex, hextoArrayBuffer } from "./index.mts";
|
|
4
|
+
|
|
5
|
+
const buf = new ArrayBuffer(3);
|
|
6
|
+
const ui8a = new Uint8Array(buf);
|
|
7
|
+
ui8a[0] = 1;
|
|
8
|
+
ui8a[2] = 1;
|
|
9
|
+
|
|
10
|
+
test("hextoArrayBuffer", () => {
|
|
11
|
+
expect(hextoArrayBuffer("010001")).toStrictEqual(buf);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("ArrayBuffertohex", () => {
|
|
15
|
+
expect(ArrayBuffertohex(buf)).toBe("010001");
|
|
16
|
+
});
|
package/src/conv_ba.mts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* convert a ASCII string to a hexadecimal string of ASCII codes.
|
|
3
|
+
* @description
|
|
4
|
+
* NOTE: This can't be used for non ASCII characters.
|
|
5
|
+
* @param s - ASCII string
|
|
6
|
+
* @return hexadecimal string
|
|
7
|
+
*/
|
|
8
|
+
export function stohex(s: string): string {
|
|
9
|
+
return BAtohex(stoBA(s));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* convert an array of bytes(Number) to hexadecimal string.
|
|
14
|
+
* @param a - a array of bytes
|
|
15
|
+
* @return hexadecimal string
|
|
16
|
+
*/
|
|
17
|
+
export function BAtohex(a: Array<number>): string {
|
|
18
|
+
let s = "";
|
|
19
|
+
for (let i = 0; i < a.length; i++) {
|
|
20
|
+
let hex1 = a[i].toString(16);
|
|
21
|
+
if (hex1.length === 1) hex1 = `0${hex1}`;
|
|
22
|
+
s = s + hex1;
|
|
23
|
+
}
|
|
24
|
+
return s;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ==== string / byte array ================================
|
|
28
|
+
/**
|
|
29
|
+
* convert a string to an array of character codes
|
|
30
|
+
* @param s - string
|
|
31
|
+
* @return array of character code value
|
|
32
|
+
*/
|
|
33
|
+
export function stoBA(s: string): Array<number> {
|
|
34
|
+
const a = new Array();
|
|
35
|
+
for (let i = 0; i < s.length; i++) {
|
|
36
|
+
a[i] = s.charCodeAt(i);
|
|
37
|
+
}
|
|
38
|
+
return a;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* convert an array of character codes to a string
|
|
43
|
+
* @param a - a array of character codes
|
|
44
|
+
* @return string
|
|
45
|
+
*/
|
|
46
|
+
export function BAtos(a: Array<number>): string {
|
|
47
|
+
let s = "";
|
|
48
|
+
for (let i = 0; i < a.length; i++) {
|
|
49
|
+
s = s + String.fromCharCode(a[i]);
|
|
50
|
+
}
|
|
51
|
+
return s;
|
|
52
|
+
}
|
package/src/conv_ip.mts
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* convert any IPv6 address to a 16 byte hexadecimal string
|
|
3
|
+
* @param sIn - ipv6 address string
|
|
4
|
+
* @return 16 byte hexadecimal string of IPv6 address
|
|
5
|
+
*
|
|
6
|
+
* @description
|
|
7
|
+
* This function converts any IPv6 address representation string
|
|
8
|
+
* to a 16 byte hexadecimal string of address.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
*/
|
|
12
|
+
export function ipv6tohex(sIn: string): string {
|
|
13
|
+
let s: string = sIn;
|
|
14
|
+
const msgMalformedAddress = "malformed IPv6 address";
|
|
15
|
+
if (!s.match(/^[0-9A-Fa-f:]+$/)) throw msgMalformedAddress;
|
|
16
|
+
|
|
17
|
+
// 1. downcase
|
|
18
|
+
s = s.toLowerCase();
|
|
19
|
+
|
|
20
|
+
// 2. expand ::
|
|
21
|
+
const num_colon = s.split(":").length - 1;
|
|
22
|
+
if (num_colon < 2) throw msgMalformedAddress;
|
|
23
|
+
const colon_replacer = ":".repeat(7 - num_colon + 2);
|
|
24
|
+
s = s.replace("::", colon_replacer);
|
|
25
|
+
|
|
26
|
+
// 3. fill zero
|
|
27
|
+
const a = s.split(":");
|
|
28
|
+
if (a.length !== 8) throw msgMalformedAddress;
|
|
29
|
+
for (let i = 0; i < 8; i++) {
|
|
30
|
+
a[i] = `0000${a[i]}`.slice(-4);
|
|
31
|
+
}
|
|
32
|
+
return a.join("");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* convert a 16 byte hexadecimal string to RFC 5952 canonicalized IPv6 address
|
|
37
|
+
* @param hex - hexadecimal string of 16 byte IPv6 address
|
|
38
|
+
* @return IPv6 address string canonicalized by RFC 5952
|
|
39
|
+
*
|
|
40
|
+
* @description
|
|
41
|
+
* This function converts a 16 byte hexadecimal string to
|
|
42
|
+
* <a href="https://tools.ietf.org/html/rfc5952">RFC 5952</a>
|
|
43
|
+
* canonicalized IPv6 address string.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* hextoipv6("871020010db8000000000000000000000004") -> "2001:db8::4"
|
|
47
|
+
* hextoipv6("871020010db8000000000000000000") -> raise exception
|
|
48
|
+
* hextoipv6("xyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxyz") -> raise exception
|
|
49
|
+
*/
|
|
50
|
+
export function hextoipv6(hex: string): string {
|
|
51
|
+
let s: string = hex;
|
|
52
|
+
if (!s.match(/^[0-9A-Fa-f]{32}$/))
|
|
53
|
+
throw new Error(`malformed IPv6 address: ${s}`);
|
|
54
|
+
|
|
55
|
+
// 1. downcase
|
|
56
|
+
s = s.toLowerCase();
|
|
57
|
+
|
|
58
|
+
// 2. split 4 > ["0123", "00a4", "0000", ..., "ffff"]
|
|
59
|
+
let a: string[] = s.match(/.{1,4}/g) || [];
|
|
60
|
+
|
|
61
|
+
// 3. trim leading 0 for items and join > "123:a4:0:...:ffff"
|
|
62
|
+
a = a.map((s) => s.replace(/^0+/, ""));
|
|
63
|
+
a = a.map((s) => (s === "" ? "0" : s));
|
|
64
|
+
s = `:${a.join(":")}:`;
|
|
65
|
+
|
|
66
|
+
// 4. find shrinkable candidates :0:0:..:0:
|
|
67
|
+
const aZero = s.match(/:(0:){2,}/g);
|
|
68
|
+
|
|
69
|
+
// 5. no shrinkable
|
|
70
|
+
if (aZero == null) return s.slice(1, -1);
|
|
71
|
+
|
|
72
|
+
// 6. fix max length zero(:0:...:0:)
|
|
73
|
+
const sMaxZero = aZero.sort().slice(-1)[0];
|
|
74
|
+
|
|
75
|
+
// 7. replace shrinked
|
|
76
|
+
s = s.replace(sMaxZero.substr(0, sMaxZero.length - 1), ":");
|
|
77
|
+
|
|
78
|
+
// 8. trim leading ':' if not '::'
|
|
79
|
+
if (s.substr(0, 2) !== "::") s = s.substr(1);
|
|
80
|
+
|
|
81
|
+
// 9. trim tail ':' if not '::'
|
|
82
|
+
if (s.substr(-2, 2) !== "::") s = s.substr(0, s.length - 1);
|
|
83
|
+
|
|
84
|
+
return s;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/*
|
|
88
|
+
* @see hextoipv6
|
|
89
|
+
* @see iptohex
|
|
90
|
+
*/
|
|
91
|
+
/**
|
|
92
|
+
* convert a hexadecimal string to IP addresss
|
|
93
|
+
* @param s - hexadecimal string of IP address
|
|
94
|
+
* @return IP address string
|
|
95
|
+
*
|
|
96
|
+
* @description
|
|
97
|
+
* This function converts a hexadecimal string of IPv4 or
|
|
98
|
+
* IPv6 address to IPv4 or IPv6 address string.
|
|
99
|
+
* If byte length is not 4 nor 16, this returns a
|
|
100
|
+
* hexadecimal string without conversion.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* hextoip("c0a80101") -> "192.168.1.1"
|
|
104
|
+
* hextoip("871020010db8000000000000000000000004") -> "2001:db8::4"
|
|
105
|
+
* hextoip("c0a80100ffffff00") -> "192.168.1.0/24"
|
|
106
|
+
* hextoip("c0a801010203") -> "c0a801010203" // wrong 6 bytes
|
|
107
|
+
* hextoip("zzz")) -> raise exception because of not hexadecimal
|
|
108
|
+
*/
|
|
109
|
+
export function hextoip(s: string): string {
|
|
110
|
+
const malformedErr = new Error("malformed hex value");
|
|
111
|
+
if (!s.match(/^([0-9A-Fa-f][0-9A-Fa-f]){1,}$/)) throw malformedErr;
|
|
112
|
+
if (s.length === 8) {
|
|
113
|
+
// ipv4
|
|
114
|
+
let ip: string;
|
|
115
|
+
try {
|
|
116
|
+
ip = `${parseInt(s.substr(0, 2), 16)}.${parseInt(
|
|
117
|
+
s.substr(2, 2),
|
|
118
|
+
16,
|
|
119
|
+
)}.${parseInt(s.substr(4, 2), 16)}.${parseInt(s.substr(6, 2), 16)}`;
|
|
120
|
+
return ip;
|
|
121
|
+
} catch (ex) {
|
|
122
|
+
throw malformedErr;
|
|
123
|
+
}
|
|
124
|
+
} else if (s.length === 16) {
|
|
125
|
+
try {
|
|
126
|
+
return `${hextoip(s.substr(0, 8))}/${ipprefixlen(s.substr(8))}`;
|
|
127
|
+
} catch (ex) {
|
|
128
|
+
throw malformedErr;
|
|
129
|
+
}
|
|
130
|
+
} else if (s.length === 32) {
|
|
131
|
+
return hextoipv6(s);
|
|
132
|
+
} else if (s.length === 64) {
|
|
133
|
+
try {
|
|
134
|
+
return `${hextoipv6(s.substr(0, 32))}/${ipprefixlen(s.substr(32))}`;
|
|
135
|
+
} catch (ex) {
|
|
136
|
+
throw malformedErr;
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
return s;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* convert IPv4/v6 addresss to a hexadecimal string
|
|
145
|
+
* @param sIn - IPv4/v6 address string
|
|
146
|
+
* @return hexadecimal string of IP address
|
|
147
|
+
* @see hextoip
|
|
148
|
+
* @see ipv6tohex
|
|
149
|
+
*
|
|
150
|
+
* @description
|
|
151
|
+
* This function converts IPv4 or IPv6 address string to
|
|
152
|
+
* a hexadecimal string of IPv4 or IPv6 address.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* iptohex("192.168.1.1") -> "c0a80101"
|
|
156
|
+
* iptohex("2001:db8::4") -> "871020010db8000000000000000000000004"
|
|
157
|
+
* iptohex("192.168.1.1/24") -> "c0a80101ffffff00"
|
|
158
|
+
* iptohex("2001:db8::/120") -> "871020010db8000000000000000000000000ffffffffffffffffffffffffffffffffff00"
|
|
159
|
+
* iptohex("zzz")) -> raise exception
|
|
160
|
+
*/
|
|
161
|
+
export function iptohex(sIn: string): string {
|
|
162
|
+
let s: string = sIn;
|
|
163
|
+
const malformedErr = new Error("malformed IP address");
|
|
164
|
+
s = s.toLowerCase();
|
|
165
|
+
|
|
166
|
+
if (!s.match(/^[0-9a-f.:/]+$/)) throw malformedErr;
|
|
167
|
+
|
|
168
|
+
if (s.match(/^[0-9.]+$/)) {
|
|
169
|
+
const a = s.split(".");
|
|
170
|
+
if (a.length !== 4) throw malformedErr;
|
|
171
|
+
let hex = "";
|
|
172
|
+
try {
|
|
173
|
+
for (let i = 0; i < 4; i++) {
|
|
174
|
+
const d = parseInt(a[i]);
|
|
175
|
+
hex += `0${d.toString(16)}`.slice(-2);
|
|
176
|
+
}
|
|
177
|
+
return hex;
|
|
178
|
+
} catch (ex) {
|
|
179
|
+
throw malformedErr;
|
|
180
|
+
}
|
|
181
|
+
} else if (s.match(/^[0-9.]+\/[0-9]+$/)) {
|
|
182
|
+
const aItem = s.split("/");
|
|
183
|
+
return iptohex(aItem[0]) + ipnetmask(parseInt(aItem[1]), 32);
|
|
184
|
+
} else if (s.match(/^[0-9a-f:]+$/) && s.indexOf(":") !== -1) {
|
|
185
|
+
return ipv6tohex(s);
|
|
186
|
+
} else if (s.match(/^[0-9a-f:]+\/[0-9]+$/) && s.indexOf(":") !== -1) {
|
|
187
|
+
const aItem = s.split("/");
|
|
188
|
+
return ipv6tohex(aItem[0]) + ipnetmask(parseInt(aItem[1]), 128);
|
|
189
|
+
} else {
|
|
190
|
+
throw malformedErr;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/*
|
|
195
|
+
* convert subnet mask hex to ip address prefix length<br/>
|
|
196
|
+
* @param hMask - hexadecimal string of ipv4/6 subnet mask (ex. "ffffff00" for v4 class C)
|
|
197
|
+
* @return ip address prefix length (ex. 24 for IPv4 class C)
|
|
198
|
+
*/
|
|
199
|
+
export function ipprefixlen(hMask: string): number {
|
|
200
|
+
const malformedErr = new Error("malformed mask");
|
|
201
|
+
let bMask: string;
|
|
202
|
+
try {
|
|
203
|
+
bMask = parseInt(hMask, 16).toString(2);
|
|
204
|
+
} catch (ex) {
|
|
205
|
+
throw malformedErr;
|
|
206
|
+
}
|
|
207
|
+
if (!bMask.match(/^1*0*$/)) throw malformedErr;
|
|
208
|
+
return bMask.replace(/0+$/, "").length;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/*
|
|
212
|
+
* convert ip prefix length to net mask octets<br/>
|
|
213
|
+
* @param prefixlen - ip prefix length value (ex. 24 for IPv4 class C)
|
|
214
|
+
* @param len - ip address length (ex. 32 for IPv4 and 128 for IPv6)
|
|
215
|
+
* @return hexadecimal string of net mask octets
|
|
216
|
+
* @example
|
|
217
|
+
* ipnetmask(24, 32) -> "ffffff00"
|
|
218
|
+
* ipnetmask(120, 128) -> "ffffffffffffffffffffffffffffff00"
|
|
219
|
+
*/
|
|
220
|
+
export function ipnetmask(prefixlen: number, len: number): string {
|
|
221
|
+
if (len === 32 && prefixlen === 0) return "00000000"; // v4
|
|
222
|
+
if (len === 128 && prefixlen === 0) return "00000000000000000000000000000000"; // v6
|
|
223
|
+
const b =
|
|
224
|
+
Array(prefixlen + 1).join("1") + Array(len - prefixlen + 1).join("0");
|
|
225
|
+
return parseInt(b, 2).toString(16);
|
|
226
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { hextoutf8, utf8tohex } from "./conv.mts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* convert a hexadecimal ISO 8859-1 latin string to UTF-8 string
|
|
5
|
+
* @param h hexadecimal ISO 8859-1 latin string
|
|
6
|
+
* @return UTF-8 string
|
|
7
|
+
* @see {@link utf8toiso88591hex}
|
|
8
|
+
* @example
|
|
9
|
+
* iso88591hextoutf8("41a9fa") -> "A©ú"
|
|
10
|
+
*/
|
|
11
|
+
export function iso88591hextoutf8(h: string): string {
|
|
12
|
+
return hextoutf8(iso88591hextoutf8hex(h));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* convert UTF-8 string to a hexadecimal ISO 8859-1 latin string
|
|
17
|
+
* @param s hexadecimal ISO 8859-1 latin string
|
|
18
|
+
* @return UTF-8 string
|
|
19
|
+
* @see {@link iso88591hextoutf8}
|
|
20
|
+
* @example
|
|
21
|
+
* utf8toiso88591hex("A©ú") -> "41a9fa"
|
|
22
|
+
*/
|
|
23
|
+
export function utf8toiso88591hex(s: string): string {
|
|
24
|
+
return utf8hextoiso88591hex(utf8tohex(s));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function iso88591hextoutf8hex(h: string): string {
|
|
28
|
+
const a = h.match(/.{1,2}/g);
|
|
29
|
+
if (a == null) return "";
|
|
30
|
+
const a2 = [];
|
|
31
|
+
for (let i = 0; i < a.length; i++) {
|
|
32
|
+
const di = parseInt(a[i], 16);
|
|
33
|
+
if (0xa1 <= di && di <= 0xbf) {
|
|
34
|
+
a2.push("c2");
|
|
35
|
+
a2.push(a[i]);
|
|
36
|
+
} else if (0xc0 <= di && di <= 0xff) {
|
|
37
|
+
a2.push("c3");
|
|
38
|
+
a2.push((di - 64).toString(16));
|
|
39
|
+
} else {
|
|
40
|
+
a2.push(a[i]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return a2.join("");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function utf8hextoiso88591hex(h: string): string {
|
|
47
|
+
const a = h.match(/.{1,2}/g);
|
|
48
|
+
if (a == null) return "";
|
|
49
|
+
const a2 = [];
|
|
50
|
+
for (let i = 0; i < a.length; i++) {
|
|
51
|
+
if (a[i] === "c2") {
|
|
52
|
+
i++;
|
|
53
|
+
a2.push(a[i]);
|
|
54
|
+
} else if (a[i] === "c3") {
|
|
55
|
+
i++;
|
|
56
|
+
const di = parseInt(a[i], 16) + 64;
|
|
57
|
+
a2.push(di.toString(16));
|
|
58
|
+
} else {
|
|
59
|
+
a2.push(a[i]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return a2.join("");
|
|
63
|
+
}
|
package/src/conv_pem.mts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { b64tohex, hextob64 } from "./conv.mts";
|
|
2
|
+
|
|
3
|
+
// === pem / hex ======================
|
|
4
|
+
/**
|
|
5
|
+
* get PEM string from hexadecimal data and header string
|
|
6
|
+
* @param dataHex - hexadecimal string of PEM body
|
|
7
|
+
* @param pemHeader - PEM header string (ex. 'RSA PRIVATE KEY')
|
|
8
|
+
* @param separator - new line string
|
|
9
|
+
* @return PEM formatted string of input data
|
|
10
|
+
*
|
|
11
|
+
* @description
|
|
12
|
+
* This function converts a hexadecimal string to a PEM string with
|
|
13
|
+
* a specified header. Its line break will be CRLF("\r\n").
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* hextopem('616161', 'RSA PRIVATE KEY') ->
|
|
17
|
+
* -----BEGIN PRIVATE KEY-----
|
|
18
|
+
* YWFh
|
|
19
|
+
* -----END PRIVATE KEY-----
|
|
20
|
+
*/
|
|
21
|
+
export function hextopem(
|
|
22
|
+
dataHex: string,
|
|
23
|
+
pemHeader: string,
|
|
24
|
+
separator = "\r\n",
|
|
25
|
+
): string {
|
|
26
|
+
return `-----BEGIN ${pemHeader}-----${separator}${foldnl(
|
|
27
|
+
hextob64(dataHex),
|
|
28
|
+
64,
|
|
29
|
+
)}${separator}-----END ${pemHeader}-----${separator}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* get hexacedimal string from PEM format data
|
|
34
|
+
* @param s - PEM formatted string
|
|
35
|
+
* @param sHead - PEM header string without BEGIN/END
|
|
36
|
+
* @return hexadecimal string data of PEM contents
|
|
37
|
+
*
|
|
38
|
+
* @description
|
|
39
|
+
* This static method gets a hexacedimal string of contents
|
|
40
|
+
* from PEM format data. You can explicitly specify PEM header
|
|
41
|
+
* by sHead argument.
|
|
42
|
+
* Any space characters such as white space or new line
|
|
43
|
+
* will be omitted.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* pemtohex("-----BEGIN PUBLIC KEY...") -> "3082..."
|
|
47
|
+
* pemtohex("-----BEGIN CERTIFICATE...", "CERTIFICATE") -> "3082..."
|
|
48
|
+
* pemtohex(" \r\n-----BEGIN DSA PRIVATE KEY...") -> "3082..."
|
|
49
|
+
* pemtohex("-----BEGIN EC PARAMETERS...----BEGIN EC PRIVATE KEY...." -> "3082..."
|
|
50
|
+
*/
|
|
51
|
+
export function pemtohex(s: string, sHead?: string): string {
|
|
52
|
+
return b64tohex(pemtob64(s, sHead));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* get Base64 string from PEM format data
|
|
57
|
+
* @param pem - PEM formatted string
|
|
58
|
+
* @param sHead - PEM header string without BEGIN/END
|
|
59
|
+
* @return Base64 string
|
|
60
|
+
*
|
|
61
|
+
* @description
|
|
62
|
+
* This static method gets a Base64 string of contents
|
|
63
|
+
* from PEM format data.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* pemtob64("-----BEGIN CERTIFICATE...", "CERTIFICATE") -> "MIIBvTCC..."
|
|
67
|
+
* pemtob64("-----BEGIN CERTIFICATE...") -> "MIIBvTCC..."
|
|
68
|
+
*/
|
|
69
|
+
export function pemtob64(pem: string, sHead?: string): string {
|
|
70
|
+
let s: string = pem;
|
|
71
|
+
if (s.indexOf("-----BEGIN ") === -1) throw new Error("can't find PEM header");
|
|
72
|
+
|
|
73
|
+
if (sHead !== undefined) {
|
|
74
|
+
s = s.replace(new RegExp(`^[^-]*-----BEGIN ${sHead}-----`), "");
|
|
75
|
+
s = s.replace(new RegExp(`-----END ${sHead}-----[^-]*$`), "");
|
|
76
|
+
} else {
|
|
77
|
+
s = s.replace(/^[^-]*-----BEGIN [^-]+-----/, "");
|
|
78
|
+
s = s.replace(/-----END [^-]+-----[^-]*$/, "");
|
|
79
|
+
}
|
|
80
|
+
return s.replace(/\s+/g, "");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// === others =========================
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* get PEM string from Base64 string
|
|
87
|
+
* @param b64 - Base64 string of PEM body
|
|
88
|
+
* @param pemHeader - PEM header string (ex. 'RSA PRIVATE KEY')
|
|
89
|
+
* @param separator - new line string
|
|
90
|
+
* @return PEM formatted string of input data
|
|
91
|
+
*
|
|
92
|
+
* @description
|
|
93
|
+
* This function converts a Base64 string to a PEM string with
|
|
94
|
+
* a specified header. Its line break will be CRLF("\r\n").
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* b64topem('YWFh', 'RSA PRIVATE KEY') ->
|
|
98
|
+
* -----BEGIN PRIVATE KEY-----
|
|
99
|
+
* YWFh
|
|
100
|
+
* -----END PRIVATE KEY-----
|
|
101
|
+
*/
|
|
102
|
+
export function b64topem(
|
|
103
|
+
b64: string,
|
|
104
|
+
pemHeader: string,
|
|
105
|
+
separator = "\r\n",
|
|
106
|
+
): string {
|
|
107
|
+
return `-----BEGIN ${pemHeader}-----${separator}${foldnl(
|
|
108
|
+
b64,
|
|
109
|
+
64,
|
|
110
|
+
separator,
|
|
111
|
+
)}${separator}-----END ${pemHeader}-----${separator}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* convert a Base64 encoded string with new lines to a hexadecimal string
|
|
116
|
+
* @param s - Base64 encoded string with new lines
|
|
117
|
+
* @return hexadecimal string
|
|
118
|
+
*
|
|
119
|
+
* @description
|
|
120
|
+
* This function converts from a Base64 encoded
|
|
121
|
+
* string with new lines to a hexadecimal string.
|
|
122
|
+
* This is useful to handle PEM encoded file.
|
|
123
|
+
* This function removes any non-Base64 characters (i.e. not 0-9,A-Z,a-z,\,+,=)
|
|
124
|
+
* including new line.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* hextob64nl(
|
|
128
|
+
* "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4\r\n" +
|
|
129
|
+
* "OTAxMjM0NTY3ODkwCg==\r\n")
|
|
130
|
+
* ->
|
|
131
|
+
* "123456789012345678901234567890123456789012345678901234567890"
|
|
132
|
+
*/
|
|
133
|
+
export function b64nltohex(s: string): string {
|
|
134
|
+
const b64 = s.replace(/[^0-9A-Za-z\/+=]*/g, "");
|
|
135
|
+
const hex = b64tohex(b64);
|
|
136
|
+
return hex;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* wrap string with new lines to fit in specified width
|
|
141
|
+
* @param sIn - string
|
|
142
|
+
* @param n - width
|
|
143
|
+
* @param separator - line separator
|
|
144
|
+
* @return wrapped string with new lines
|
|
145
|
+
*
|
|
146
|
+
* @description
|
|
147
|
+
* This function wrap a string with new lines to fit in specified width.
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* foldnl("1234567890", 6)
|
|
151
|
+
* ->
|
|
152
|
+
* 123456
|
|
153
|
+
* 7890
|
|
154
|
+
*/
|
|
155
|
+
export function foldnl(sIn: string, n: number, separator = "\r\n"): string {
|
|
156
|
+
let s: string = sIn;
|
|
157
|
+
s = s.replace(new RegExp(`(.{${n}})`, "g"), `$1${separator}`);
|
|
158
|
+
s = s.replace(/\s+$/, "");
|
|
159
|
+
return s;
|
|
160
|
+
}
|