···13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1516-let max = 10000000000000; // biggest 10^n integer that can still fit 2^53 when multiplied by 256
01718export class Int10 {
19 /**
···2627 /**
28 * Multiply value by m and add c.
29- * @param {number} m - multiplier, must be < =256
30- * @param {number} c - value to add
31 */
32 mulAdd(m, c) {
033 // assert(m <= 256)
034 let b = this.buf,
35 l = b.length,
36 i, t;
···7172 /**
73 * Convert to decimal string representation.
74- * @param {*} base - optional value, only value accepted is 10
075 */
76- toString(base) {
77- if ((base || 10) != 10)
78- throw 'only base 10 is supported';
79 let b = this.buf,
80 s = b[b.length - 1].toString();
81 for (let i = b.length - 2; i >= 0; --i)
···86 /**
87 * Convert to Number value representation.
88 * Will probably overflow 2^53 and thus become approximate.
089 */
90 valueOf() {
91 let b = this.buf,
···9798 /**
99 * Return value as a simple Number (if it is <= 10000000000000), or return this.
0100 */
101 simplify() {
102 let b = this.buf;
···13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1516+/** Biggest 10^n integer that can still fit 2^53 when multiplied by 256. */
17+const max = 10000000000000;
1819export class Int10 {
20 /**
···2728 /**
29 * Multiply value by m and add c.
30+ * @param {number} m - multiplier, must be 0<m<=256
31+ * @param {number} c - value to add, must be c>=0
32 */
33 mulAdd(m, c) {
34+ // assert(m > 0)
35 // assert(m <= 256)
36+ // assert(c >= 0)
37 let b = this.buf,
38 l = b.length,
39 i, t;
···7475 /**
76 * Convert to decimal string representation.
77+ * @param {number} [base=10] - optional value, only value accepted is 10
78+ * @returns {string} The decimal string representation.
79 */
80+ toString(base = 10) {
81+ if (base != 10)
82+ throw new Error('only base 10 is supported');
83 let b = this.buf,
84 s = b[b.length - 1].toString();
85 for (let i = b.length - 2; i >= 0; --i)
···90 /**
91 * Convert to Number value representation.
92 * Will probably overflow 2^53 and thus become approximate.
93+ * @returns {number} The numeric value.
94 */
95 valueOf() {
96 let b = this.buf,
···102103 /**
104 * Return value as a simple Number (if it is <= 10000000000000), or return this.
105+ * @returns {number | Int10} The simplified value.
106 */
107 simplify() {
108 let b = this.buf;
+121-59
test.js
···3import { ASN1, Stream } from './asn1.js';
4import { Hex } from './hex.js';
5import { Base64 } from './base64.js';
067-const
8- all = (process.argv[2] == 'all');
00000000000000000000000910-const tests = [
000000000000000000000000000000000000000000000000000000011 // RSA Laboratories technical notes from https://luca.ntop.org/Teaching/Appunti/asn1.html
12 ['0304066E5DC0', '(18 bit)\n011011100101110111', 'ntop, bit string: DER encoding'],
13 ['0304066E5DE0', '(18 bit)\n011011100101110111', 'ntop, bit string: padded with "100000"'],
···89 ['181331393835313130363231303632372E332B3134', '1985-11-06 21:06:27.3 UTC+14:00', 'UTC offset +13 and +14'], // GitHub issue #54
90 ['032100171E83C1B251803F86DD01E9CFA886BE89A7316D8372649AC2231EC669F81A84', n => { if (n.sub != null) return 'Should not decode content: ' + n.sub[0].content(); }, 'Key that resembles an UTCTime'], // GitHub issue #79
91 ['171E83C1B251803F86DD01E9CFA886BE89A7316D8372649AC2231EC669F81A84', /^Exception:\nError: Unrecognized time: /, 'Invalid UTCTime'], // GitHub issue #79
92-];
9394-const testsB64 = [
0000000000095 'AA==',
96 'ABA=',
97 'ABCD',
···99 'ABCDEFE=',
100 'ABCDEFGH',
101 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQR\nSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456w==',
102-];
103104-function check(result, expected, comment) {
105- if (!result || result == expected) {
106- if (all) console.log('\x1B[1m\x1B[32mOK \x1B[39m\x1B[22m ' + comment);
107- return true;
108- } else {
109- console.log('\x1B[1m\x1B[31mERR\x1B[39m\x1B[22m ' + comment);
110- console.log(' \x1B[1m\x1B[34mEXP\x1B[39m\x1B[22m ' + expected.toString().replace(/\n/g, '\n '));
111- console.log(' \x1B[1m\x1B[33mGOT\x1B[39m\x1B[22m ' + result.replace(/\n/g, '\n '));
112- return false;
113- }
114-}
000000000115116-let
117- run = 0,
118- expErr = 0,
119- error = 0;
120-for (let t of tests) {
121- const input = t[0],
122- expected = t[1],
123- comment = t[2];
124- let result;
125- try {
126- let node = ASN1.decode(Hex.decode(input));
127- if (typeof expected == 'function')
128- result = expected(node);
129- else
130- result = node.content();
131- //TODO: check structure, not only first level content
132- } catch (e) {
133- result = 'Exception:\n' + e;
134- }
135- if (expected instanceof RegExp)
136- result = expected.test(result) ? null : 'does not match';
137- ++run;
138- if (!check(result, expected, comment))
139- ++error;
140-}
141-for (let t of testsB64) {
142- let bin = Base64.decode(t);
143- let url = new Stream(bin, 0).b64Dump(0, bin.length);
144- ++run;
145- if (!check(url, t.replace(/\n/g, '').replace(/=*$/g, ''), 'Base64url: ' + bin.length + ' bytes'))
146- ++error;
147- let pretty = Base64.pretty(url);
148- ++run;
149- if (!check(pretty, t, 'Base64pretty: ' + bin.length + ' bytes'))
150- ++error;
151- let std = new Stream(bin, 0).b64Dump(0, bin.length, 'std');
152- ++run;
153- if (!check(std, t.replace(/\n/g, ''), 'Base64: ' + bin.length + ' bytes'))
154- ++error;
155-}
156-console.log(run + ' tested, ' + expErr + ' expected, ' + error + ' errors.');
157-process.exit(error ? 1 : 0);
···3import { ASN1, Stream } from './asn1.js';
4import { Hex } from './hex.js';
5import { Base64 } from './base64.js';
6+import { Int10 } from './int10.js';
78+const all = (process.argv[2] == 'all');
9+10+/** @type {Array<Tests>} */
11+const tests = [];
12+13+const stats = {
14+ run: 0,
15+ error: 0,
16+};
17+18+/**
19+ * A class for managing and executing tests.
20+ */
21+class Tests {
22+ /**
23+ * An array to store test data.
24+ * @type {Array<unknown>}
25+ */
26+ data;
27+28+ /**
29+ * Checks a row of test data.
30+ * @param {Function} t - How to test a row of data.
31+ */
32+ checkRow;
3334+ /**
35+ * Constructs a new Tests instance.
36+ * @param {Function} checkRow - A function to check each row of data.
37+ * @param {Array<unknown>} data - The test data to be processed.
38+ */
39+ constructor(checkRow, data) {
40+ this.checkRow = checkRow;
41+ this.data = data;
42+ }
43+44+ /**
45+ * Executes the tests and checks their results for all rows.
46+ */
47+ checkAll() {
48+ for (const t of this.data)
49+ this.checkRow(t);
50+ }
51+52+ /**
53+ * Prints the result of a test, indicating if it passed or failed.
54+ * @param {unknown} result The actual result of the test.
55+ * @param {unknown} expected The expected result of the test.
56+ * @param {string} comment A comment describing the test.
57+ */
58+ checkResult(result, expected, comment) {
59+ ++stats.run;
60+ if (!result || result == expected) {
61+ if (all) console.log('\x1B[1m\x1B[32mOK \x1B[39m\x1B[22m ' + comment);
62+ } else {
63+ ++stats.error;
64+ console.log('\x1B[1m\x1B[31mERR\x1B[39m\x1B[22m ' + comment);
65+ console.log(' \x1B[1m\x1B[34mEXP\x1B[39m\x1B[22m ' + expected.toString().replace(/\n/g, '\n '));
66+ console.log(' \x1B[1m\x1B[33mGOT\x1B[39m\x1B[22m ' + result.replace(/\n/g, '\n '));
67+ }
68+ }
69+}
70+71+tests.push(new Tests(function (t) {
72+ const input = t[0],
73+ expected = t[1],
74+ comment = t[2];
75+ let result;
76+ try {
77+ let node = ASN1.decode(Hex.decode(input));
78+ if (typeof expected == 'function')
79+ result = expected(node);
80+ else
81+ result = node.content();
82+ //TODO: check structure, not only first level content
83+ } catch (e) {
84+ result = 'Exception:\n' + e;
85+ }
86+ if (expected instanceof RegExp)
87+ result = expected.test(result) ? null : 'does not match';
88+ this.checkResult(result, expected, comment);
89+}, [
90 // RSA Laboratories technical notes from https://luca.ntop.org/Teaching/Appunti/asn1.html
91 ['0304066E5DC0', '(18 bit)\n011011100101110111', 'ntop, bit string: DER encoding'],
92 ['0304066E5DE0', '(18 bit)\n011011100101110111', 'ntop, bit string: padded with "100000"'],
···168 ['181331393835313130363231303632372E332B3134', '1985-11-06 21:06:27.3 UTC+14:00', 'UTC offset +13 and +14'], // GitHub issue #54
169 ['032100171E83C1B251803F86DD01E9CFA886BE89A7316D8372649AC2231EC669F81A84', n => { if (n.sub != null) return 'Should not decode content: ' + n.sub[0].content(); }, 'Key that resembles an UTCTime'], // GitHub issue #79
170 ['171E83C1B251803F86DD01E9CFA886BE89A7316D8372649AC2231EC669F81A84', /^Exception:\nError: Unrecognized time: /, 'Invalid UTCTime'], // GitHub issue #79
171+]));
172173+tests.push(new Tests(function (t) {
174+ let bin = Base64.decode(t);
175+ let url = new Stream(bin, 0).b64Dump(0, bin.length);
176+ // check base64url encoding
177+ this.checkResult(url, t.replace(/\n/g, '').replace(/=*$/g, ''), 'Base64url: ' + bin.length + ' bytes');
178+ // check conversion from base64url to base64
179+ let pretty = Base64.pretty(url);
180+ this.checkResult(pretty, t, 'Base64pretty: ' + bin.length + ' bytes');
181+ let std = new Stream(bin, 0).b64Dump(0, bin.length, 'std');
182+ // check direct base64 encoding
183+ this.checkResult(std, t.replace(/\n/g, ''), 'Base64: ' + bin.length + ' bytes');
184+}, [
185 'AA==',
186 'ABA=',
187 'ABCD',
···189 'ABCDEFE=',
190 'ABCDEFGH',
191 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQR\nSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456w==',
192+]));
193194+tests.push(new Tests(function (t) {
195+ this.row = (0|this.row) + 1;
196+ this.num = this.num || new Int10();
197+ this.num.mulAdd(t[0], t[1]);
198+ this.checkResult(this.num.toString(), t[2], 'Int10 row ' + this.row);
199+}, [
200+ [0, 1000000000, '1000000000'],
201+ [256, 23, '256000000023'],
202+ [256, 23, '65536000005911'],
203+ [256, 23, '16777216001513239'],
204+ [256, 23, '4294967296387389207'],
205+ [256, 23, '1099511627875171637015'],
206+ [256, 23, '281474976736043939075863'],
207+ [253, 1, '71213169114219116586193340'],
208+ [253, 1, '18016931785897436496306915021'],
209+ [253, 1, '4558283741832051433565649500314'],
210+ [253, 1, '1153245786683509012692109323579443'],
211+ [253, 1, '291771184030927780211103658865599080'],
212+ [1, 0, '291771184030927780211103658865599080'],
213+]));
214215+for (const t of tests)
216+ t.checkAll();
217+218+console.log(stats.run + ' tested, ' + stats.error + ' errors.');
219+process.exit(stats.error ? 1 : 0);
0000000000000000000000000000000000000