···1313// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1414// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15151616-let max = 10000000000000; // biggest 10^n integer that can still fit 2^53 when multiplied by 256
1616+/** Biggest 10^n integer that can still fit 2^53 when multiplied by 256. */
1717+const max = 10000000000000;
17181819export class Int10 {
1920 /**
···26272728 /**
2829 * Multiply value by m and add c.
2929- * @param {number} m - multiplier, must be < =256
3030- * @param {number} c - value to add
3030+ * @param {number} m - multiplier, must be 0<m<=256
3131+ * @param {number} c - value to add, must be c>=0
3132 */
3233 mulAdd(m, c) {
3434+ // assert(m > 0)
3335 // assert(m <= 256)
3636+ // assert(c >= 0)
3437 let b = this.buf,
3538 l = b.length,
3639 i, t;
···71747275 /**
7376 * Convert to decimal string representation.
7474- * @param {*} base - optional value, only value accepted is 10
7777+ * @param {number} [base=10] - optional value, only value accepted is 10
7878+ * @returns {string} The decimal string representation.
7579 */
7676- toString(base) {
7777- if ((base || 10) != 10)
7878- throw 'only base 10 is supported';
8080+ toString(base = 10) {
8181+ if (base != 10)
8282+ throw new Error('only base 10 is supported');
7983 let b = this.buf,
8084 s = b[b.length - 1].toString();
8185 for (let i = b.length - 2; i >= 0; --i)
···8690 /**
8791 * Convert to Number value representation.
8892 * Will probably overflow 2^53 and thus become approximate.
9393+ * @returns {number} The numeric value.
8994 */
9095 valueOf() {
9196 let b = this.buf,
···9710298103 /**
99104 * Return value as a simple Number (if it is <= 10000000000000), or return this.
105105+ * @returns {number | Int10} The simplified value.
100106 */
101107 simplify() {
102108 let b = this.buf;
+121-59
test.js
···33import { ASN1, Stream } from './asn1.js';
44import { Hex } from './hex.js';
55import { Base64 } from './base64.js';
66+import { Int10 } from './int10.js';
6777-const
88- all = (process.argv[2] == 'all');
88+const all = (process.argv[2] == 'all');
99+1010+/** @type {Array<Tests>} */
1111+const tests = [];
1212+1313+const stats = {
1414+ run: 0,
1515+ error: 0,
1616+};
1717+1818+/**
1919+ * A class for managing and executing tests.
2020+ */
2121+class Tests {
2222+ /**
2323+ * An array to store test data.
2424+ * @type {Array<unknown>}
2525+ */
2626+ data;
2727+2828+ /**
2929+ * Checks a row of test data.
3030+ * @param {Function} t - How to test a row of data.
3131+ */
3232+ checkRow;
9331010-const tests = [
3434+ /**
3535+ * Constructs a new Tests instance.
3636+ * @param {Function} checkRow - A function to check each row of data.
3737+ * @param {Array<unknown>} data - The test data to be processed.
3838+ */
3939+ constructor(checkRow, data) {
4040+ this.checkRow = checkRow;
4141+ this.data = data;
4242+ }
4343+4444+ /**
4545+ * Executes the tests and checks their results for all rows.
4646+ */
4747+ checkAll() {
4848+ for (const t of this.data)
4949+ this.checkRow(t);
5050+ }
5151+5252+ /**
5353+ * Prints the result of a test, indicating if it passed or failed.
5454+ * @param {unknown} result The actual result of the test.
5555+ * @param {unknown} expected The expected result of the test.
5656+ * @param {string} comment A comment describing the test.
5757+ */
5858+ checkResult(result, expected, comment) {
5959+ ++stats.run;
6060+ if (!result || result == expected) {
6161+ if (all) console.log('\x1B[1m\x1B[32mOK \x1B[39m\x1B[22m ' + comment);
6262+ } else {
6363+ ++stats.error;
6464+ console.log('\x1B[1m\x1B[31mERR\x1B[39m\x1B[22m ' + comment);
6565+ console.log(' \x1B[1m\x1B[34mEXP\x1B[39m\x1B[22m ' + expected.toString().replace(/\n/g, '\n '));
6666+ console.log(' \x1B[1m\x1B[33mGOT\x1B[39m\x1B[22m ' + result.replace(/\n/g, '\n '));
6767+ }
6868+ }
6969+}
7070+7171+tests.push(new Tests(function (t) {
7272+ const input = t[0],
7373+ expected = t[1],
7474+ comment = t[2];
7575+ let result;
7676+ try {
7777+ let node = ASN1.decode(Hex.decode(input));
7878+ if (typeof expected == 'function')
7979+ result = expected(node);
8080+ else
8181+ result = node.content();
8282+ //TODO: check structure, not only first level content
8383+ } catch (e) {
8484+ result = 'Exception:\n' + e;
8585+ }
8686+ if (expected instanceof RegExp)
8787+ result = expected.test(result) ? null : 'does not match';
8888+ this.checkResult(result, expected, comment);
8989+}, [
1190 // RSA Laboratories technical notes from https://luca.ntop.org/Teaching/Appunti/asn1.html
1291 ['0304066E5DC0', '(18 bit)\n011011100101110111', 'ntop, bit string: DER encoding'],
1392 ['0304066E5DE0', '(18 bit)\n011011100101110111', 'ntop, bit string: padded with "100000"'],
···89168 ['181331393835313130363231303632372E332B3134', '1985-11-06 21:06:27.3 UTC+14:00', 'UTC offset +13 and +14'], // GitHub issue #54
90169 ['032100171E83C1B251803F86DD01E9CFA886BE89A7316D8372649AC2231EC669F81A84', n => { if (n.sub != null) return 'Should not decode content: ' + n.sub[0].content(); }, 'Key that resembles an UTCTime'], // GitHub issue #79
91170 ['171E83C1B251803F86DD01E9CFA886BE89A7316D8372649AC2231EC669F81A84', /^Exception:\nError: Unrecognized time: /, 'Invalid UTCTime'], // GitHub issue #79
9292-];
171171+]));
931729494-const testsB64 = [
173173+tests.push(new Tests(function (t) {
174174+ let bin = Base64.decode(t);
175175+ let url = new Stream(bin, 0).b64Dump(0, bin.length);
176176+ // check base64url encoding
177177+ this.checkResult(url, t.replace(/\n/g, '').replace(/=*$/g, ''), 'Base64url: ' + bin.length + ' bytes');
178178+ // check conversion from base64url to base64
179179+ let pretty = Base64.pretty(url);
180180+ this.checkResult(pretty, t, 'Base64pretty: ' + bin.length + ' bytes');
181181+ let std = new Stream(bin, 0).b64Dump(0, bin.length, 'std');
182182+ // check direct base64 encoding
183183+ this.checkResult(std, t.replace(/\n/g, ''), 'Base64: ' + bin.length + ' bytes');
184184+}, [
95185 'AA==',
96186 'ABA=',
97187 'ABCD',
···99189 'ABCDEFE=',
100190 'ABCDEFGH',
101191 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQR\nSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456w==',
102102-];
192192+]));
103193104104-function check(result, expected, comment) {
105105- if (!result || result == expected) {
106106- if (all) console.log('\x1B[1m\x1B[32mOK \x1B[39m\x1B[22m ' + comment);
107107- return true;
108108- } else {
109109- console.log('\x1B[1m\x1B[31mERR\x1B[39m\x1B[22m ' + comment);
110110- console.log(' \x1B[1m\x1B[34mEXP\x1B[39m\x1B[22m ' + expected.toString().replace(/\n/g, '\n '));
111111- console.log(' \x1B[1m\x1B[33mGOT\x1B[39m\x1B[22m ' + result.replace(/\n/g, '\n '));
112112- return false;
113113- }
114114-}
194194+tests.push(new Tests(function (t) {
195195+ this.row = (0|this.row) + 1;
196196+ this.num = this.num || new Int10();
197197+ this.num.mulAdd(t[0], t[1]);
198198+ this.checkResult(this.num.toString(), t[2], 'Int10 row ' + this.row);
199199+}, [
200200+ [0, 1000000000, '1000000000'],
201201+ [256, 23, '256000000023'],
202202+ [256, 23, '65536000005911'],
203203+ [256, 23, '16777216001513239'],
204204+ [256, 23, '4294967296387389207'],
205205+ [256, 23, '1099511627875171637015'],
206206+ [256, 23, '281474976736043939075863'],
207207+ [253, 1, '71213169114219116586193340'],
208208+ [253, 1, '18016931785897436496306915021'],
209209+ [253, 1, '4558283741832051433565649500314'],
210210+ [253, 1, '1153245786683509012692109323579443'],
211211+ [253, 1, '291771184030927780211103658865599080'],
212212+ [1, 0, '291771184030927780211103658865599080'],
213213+]));
115214116116-let
117117- run = 0,
118118- expErr = 0,
119119- error = 0;
120120-for (let t of tests) {
121121- const input = t[0],
122122- expected = t[1],
123123- comment = t[2];
124124- let result;
125125- try {
126126- let node = ASN1.decode(Hex.decode(input));
127127- if (typeof expected == 'function')
128128- result = expected(node);
129129- else
130130- result = node.content();
131131- //TODO: check structure, not only first level content
132132- } catch (e) {
133133- result = 'Exception:\n' + e;
134134- }
135135- if (expected instanceof RegExp)
136136- result = expected.test(result) ? null : 'does not match';
137137- ++run;
138138- if (!check(result, expected, comment))
139139- ++error;
140140-}
141141-for (let t of testsB64) {
142142- let bin = Base64.decode(t);
143143- let url = new Stream(bin, 0).b64Dump(0, bin.length);
144144- ++run;
145145- if (!check(url, t.replace(/\n/g, '').replace(/=*$/g, ''), 'Base64url: ' + bin.length + ' bytes'))
146146- ++error;
147147- let pretty = Base64.pretty(url);
148148- ++run;
149149- if (!check(pretty, t, 'Base64pretty: ' + bin.length + ' bytes'))
150150- ++error;
151151- let std = new Stream(bin, 0).b64Dump(0, bin.length, 'std');
152152- ++run;
153153- if (!check(std, t.replace(/\n/g, ''), 'Base64: ' + bin.length + ' bytes'))
154154- ++error;
155155-}
156156-console.log(run + ' tested, ' + expErr + ' expected, ' + error + ' errors.');
157157-process.exit(error ? 1 : 0);
215215+for (const t of tests)
216216+ t.checkAll();
217217+218218+console.log(stats.run + ' tested, ' + stats.error + ' errors.');
219219+process.exit(stats.error ? 1 : 0);