From e11daf053f48265802b670e0f53e753341415871 Mon Sep 17 00:00:00 2001 From: Mohammad Shokuienia Date: Fri, 7 Dec 2018 00:11:50 -0800 Subject: [PATCH 1/6] Encoding INTEGER as an unsigned type --- index.js | 3 ++- test/index.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index b570c7e..8b42b83 100644 --- a/index.js +++ b/index.js @@ -64,7 +64,8 @@ const decoders = { const encoders = { 'INTEGER': (value) => { - const length = (Math.log2(value) >> 3) + 1; + let length = (Math.log2(value) >> 3) + 1; + length += value >> ((length * 8) - 1); const buffer = Buffer.allocUnsafe(length); buffer.writeUIntBE(value, 0, length); return buffer; diff --git a/test/index.js b/test/index.js index 5c995b7..54ae717 100644 --- a/test/index.js +++ b/test/index.js @@ -107,6 +107,21 @@ test('fromFree: decoding INTEGER', (t) => { t.is(asn1Mapper.fromTree(tree, definition), mapped); }); +test('fromFree: decoding INTEGER (unsigned decoding)', (t) => { + const tree = { + cls: CLS_UNIVERSAL, + form: FORM_PRIMITIVE, + tagCode: TAG_INTEGER, + value: Buffer.from([ 0x0, 0x92, 0x34 ]) + }; + const definition = { + type: 'INTEGER' + }; + const mapped = 0x9234; + + t.is(asn1Mapper.fromTree(tree, definition), mapped); +}); + test('fromFree: decoding NULL', (t) => { const tree = { cls: CLS_UNIVERSAL, @@ -513,6 +528,21 @@ test('toTree: encoding INTEGER', (t) => { t.deepEqual(asn1Mapper.toTree(mapped, definition), tree); }); +test('toTree: encoding unsigned INTEGER', (t) => { + const mapped = 0x9234; + const definition = { + type: 'INTEGER' + }; + const tree = { + cls: CLS_UNIVERSAL, + form: FORM_PRIMITIVE, + tagCode: TAG_INTEGER, + value: Buffer.from([ 0x0, 0x92, 0x34 ]) + }; + + t.deepEqual(asn1Mapper.toTree(mapped, definition), tree); +}); + test('toTree: encoding NULL', (t) => { const mapped = true; const definition = { From ddebc94b785398b0a8af06e2842a89a187b0e884 Mon Sep 17 00:00:00 2001 From: Mohammad Shokuienia Date: Fri, 7 Dec 2018 00:12:52 -0800 Subject: [PATCH 2/6] Applying ASN qualifiers while encoding --- index.js | 24 ++++++++++++++++++- test/index.js | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 8b42b83..7f9dac4 100644 --- a/index.js +++ b/index.js @@ -50,6 +50,17 @@ const constructedTypeMap = { 'CHOICE': 1 }; +const qualifiers = { + 'INTEGER': (value, range) => { + const match = /\(([\d]+)\.\.([\d]+)\)/g.exec(range); + return match ? value >= match[1] && value <= match[2] : true; + }, + 'OCTET STRING': (length, range) => { + const match = /^\(SIZE\(([\d]+)[\.\.]*([\d]+)*\)\)/g.exec(range); + return match ? match[2] ? length >= match[1] && length <= match[2] : length == match[1] : true; + }, +}; + const decoders = { 'INTEGER': (value) => { return value.readIntBE(0, value.length); @@ -63,13 +74,24 @@ const decoders = { }; const encoders = { - 'INTEGER': (value) => { + 'INTEGER': (value, definition) => { + if (definition.qualifiers && !qualifiers['INTEGER'](value, definition.qualifiers)) { + throw new Error('OUT_OF_RANGE_VALUE'); + } + let length = (Math.log2(value) >> 3) + 1; length += value >> ((length * 8) - 1); const buffer = Buffer.allocUnsafe(length); buffer.writeUIntBE(value, 0, length); return buffer; }, + 'OCTET STRING': (value, definition) => { + if (definition.qualifiers && !qualifiers['OCTET STRING'](value.length, definition.qualifiers)) { + throw new Error('OUT_OF_RANGE_VALUE'); + } + + return value; + }, 'NULL': (value) => value ? Buffer.from([]) : null, 'ENUMERATED': (value, definition) => { const item = definition.values.find((item) => value === item.name); diff --git a/test/index.js b/test/index.js index 54ae717..6d675d2 100644 --- a/test/index.js +++ b/test/index.js @@ -513,6 +513,44 @@ test('toTree: universal primitive', (t) => { t.deepEqual(asn1Mapper.toTree(mapped, definition), tree); }); +test('toTree: encoding qualified OCTET STRING', (t) => { + const mapped = Buffer.from([ 1, 2, 3 ]); + const definition = { + type: 'OCTET STRING', + qualifiers: '(SIZE(3))' + }; + const tree = { + cls: CLS_UNIVERSAL, + form: FORM_PRIMITIVE, + tagCode: TAG_OCTET_STRING, + value: Buffer.from([ 1, 2, 3 ]) + }; + + t.deepEqual(asn1Mapper.toTree(mapped, definition), tree); +}); + +test('toTree: encoding unqualified OCTET STRING', (t) => { + const mapped = Buffer.from([ 1, 2, 3 ]); + const definition = { + type: 'OCTET STRING', + qualifiers: '(SIZE(5))' + }; + + const error = t.throws(() => asn1Mapper.toTree(mapped, definition)); + t.is(error.message, 'OUT_OF_RANGE_VALUE'); +}); + +test('toTree: encoding unqualified range of OCTET STRING', (t) => { + const mapped = Buffer.from([ 1, 2, 3 ]); + const definition = { + type: 'OCTET STRING', + qualifiers: '(SIZE(4..8))' + }; + + const error = t.throws(() => asn1Mapper.toTree(mapped, definition)); + t.is(error.message, 'OUT_OF_RANGE_VALUE'); +}); + test('toTree: encoding INTEGER', (t) => { const mapped = 0x1234; const definition = { @@ -543,6 +581,33 @@ test('toTree: encoding unsigned INTEGER', (t) => { t.deepEqual(asn1Mapper.toTree(mapped, definition), tree); }); +test('toTree: encoding INTEGER with qualifier', (t) => { + const mapped = 0x92; + const definition = { + type: 'INTEGER', + qualifiers: '(1..255)' + }; + const tree = { + cls: CLS_UNIVERSAL, + form: FORM_PRIMITIVE, + tagCode: TAG_INTEGER, + value: Buffer.from([ 0x0, 0x92 ]) + }; + + t.deepEqual(asn1Mapper.toTree(mapped, definition), tree); +}); + +test('toTree: encoding unqualified INTEGER velue', (t) => { + const mapped = 0x0; + const definition = { + type: 'INTEGER', + qualifiers: '(1..255)' + }; + + const error = t.throws(() => asn1Mapper.toTree(mapped, definition)); + t.is(error.message, 'OUT_OF_RANGE_VALUE'); +}); + test('toTree: encoding NULL', (t) => { const mapped = true; const definition = { From 76c14b9851d1434f69622ec4ac4fbbb888e3c6ed Mon Sep 17 00:00:00 2001 From: Mohammad Shokuienia Date: Sun, 9 Dec 2018 14:22:32 -0800 Subject: [PATCH 3/6] Adding more test for better coverage --- .gitignore | 1 + package.json | 2 +- test/index.js | 30 ++++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 61b2c59..14423a0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules .nyc_output coverage .DS_Store +package-lock.json diff --git a/package.json b/package.json index cb7d5e3..bf40a62 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ }, "homepage": "https://github.com/atesgoral/node-asn1-mapper#readme", "devDependencies": { - "ava": "1.0.0-beta.4", + "ava": "^0.25.0", "coveralls": "^2.13.1", "nyc": "^11.1.0" } diff --git a/test/index.js b/test/index.js index 6d675d2..cf79818 100644 --- a/test/index.js +++ b/test/index.js @@ -477,6 +477,36 @@ test('fromTree: CHOICE where element is CLS_CONTEXT_SPECIFIC and second choice m t.deepEqual(asn1Mapper.fromTree(tree, definition), mapped); }); +test('fromTree: CHOICE where element is CLS_CONTEXT_SPECIFIC and no choice matches', (t) => { + const tree = { + cls: CLS_CONTEXT_SPECIFIC, + form: FORM_CONSTRUCTED, + tagCode: 3, + elements: [{ + cls: CLS_CONTEXT_SPECIFIC, + form: FORM_PRIMITIVE, + tagCode: 2, + value: Buffer.from([1, 2, 3]) + }] + }; + const definition = { + type: 'CHOICE', + tag: 3, + elements: [{ + name: 'foo', + tag: 0, + type: 'NULL' + }, { + name: 'bar', + tag: 1, + type: 'OCTET STRING' + }] + }; + + const error = t.throws(() => asn1Mapper.fromTree(tree, definition)); + t.is(error.message, 'No definitions found for element.'); +}); + test('fromTree: CHOICE where no choices match', (t) => { const tree = { cls: CLS_UNIVERSAL, From 61c626f7f2f8c8194411a458f2d3c2f535b40ffa Mon Sep 17 00:00:00 2001 From: Mohammad Shokuienia Date: Mon, 10 Dec 2018 09:23:12 -0800 Subject: [PATCH 4/6] More tests for better code coverage --- test/index.js | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/test/index.js b/test/index.js index cf79818..01f397d 100644 --- a/test/index.js +++ b/test/index.js @@ -559,6 +559,22 @@ test('toTree: encoding qualified OCTET STRING', (t) => { t.deepEqual(asn1Mapper.toTree(mapped, definition), tree); }); +test('toTree: encoding OCTET STRING while qualifier is invalid', (t) => { + const mapped = Buffer.from([ 1, 2, 3 ]); + const definition = { + type: 'OCTET STRING', + qualifiers: '(SIZE(-3))' + }; + const tree = { + cls: CLS_UNIVERSAL, + form: FORM_PRIMITIVE, + tagCode: TAG_OCTET_STRING, + value: Buffer.from([ 1, 2, 3 ]) + }; + + t.deepEqual(asn1Mapper.toTree(mapped, definition), tree); +}); + test('toTree: encoding unqualified OCTET STRING', (t) => { const mapped = Buffer.from([ 1, 2, 3 ]); const definition = { @@ -627,7 +643,23 @@ test('toTree: encoding INTEGER with qualifier', (t) => { t.deepEqual(asn1Mapper.toTree(mapped, definition), tree); }); -test('toTree: encoding unqualified INTEGER velue', (t) => { +test('toTree: encoding INTEGER with invalid qualifier', (t) => { + const mapped = 0x92; + const definition = { + type: 'INTEGER', + qualifiers: '(1..-255)' + }; + const tree = { + cls: CLS_UNIVERSAL, + form: FORM_PRIMITIVE, + tagCode: TAG_INTEGER, + value: Buffer.from([ 0x0, 0x92 ]) + }; + + t.deepEqual(asn1Mapper.toTree(mapped, definition), tree); +}); + +test('toTree: encoding unqualified INTEGER value', (t) => { const mapped = 0x0; const definition = { type: 'INTEGER', From d8a3bd56c50bde86599a1279c773b8b233a228ec Mon Sep 17 00:00:00 2001 From: Mohammad Shokuienia Date: Mon, 10 Dec 2018 11:00:54 -0800 Subject: [PATCH 5/6] Moving toward 100% coverage --- test/index.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/index.js b/test/index.js index 01f397d..aa10285 100644 --- a/test/index.js +++ b/test/index.js @@ -597,6 +597,23 @@ test('toTree: encoding unqualified range of OCTET STRING', (t) => { t.is(error.message, 'OUT_OF_RANGE_VALUE'); }); +test('toTree: encoding OCTET STRING with qualified length', (t) => { + const mapped = Buffer.from([ 1, 2, 3, 4, 5 ]); + const definition = { + type: 'OCTET STRING', + qualifiers: '(SIZE(4..8))' + }; + + const tree = { + cls: CLS_UNIVERSAL, + form: FORM_PRIMITIVE, + tagCode: TAG_OCTET_STRING, + value: Buffer.from([ 1, 2, 3, 4, 5 ]) + }; + + t.deepEqual(asn1Mapper.toTree(mapped, definition), tree); +}); + test('toTree: encoding INTEGER', (t) => { const mapped = 0x1234; const definition = { From 64f5c00fcb7ef412992ace98745df84cc860b558 Mon Sep 17 00:00:00 2001 From: Mohammad Shokuienia Date: Mon, 10 Dec 2018 11:17:58 -0800 Subject: [PATCH 6/6] Adding more tests for universal primitives --- test/index.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/index.js b/test/index.js index aa10285..d604da6 100644 --- a/test/index.js +++ b/test/index.js @@ -13,6 +13,7 @@ const TAG_OCTET_STRING = 4; const TAG_NULL = 5; const TAG_ENUMERATED = 10; const TAG_SEQUENCE = 16; +const TAG_IA5STRING = 22; test('fromTree: class mismatch', (t) => { const tree = { @@ -543,6 +544,21 @@ test('toTree: universal primitive', (t) => { t.deepEqual(asn1Mapper.toTree(mapped, definition), tree); }); +test('toTree: universal string primitive', (t) => { + const mapped = Buffer.from([ 1, 2, 3 ]); + const definition = { + type: 'IA5String' + }; + const tree = { + cls: CLS_UNIVERSAL, + form: FORM_PRIMITIVE, + tagCode: TAG_IA5STRING, + value: Buffer.from([ 1, 2, 3 ]) + }; + + t.deepEqual(asn1Mapper.toTree(mapped, definition), tree); +}); + test('toTree: encoding qualified OCTET STRING', (t) => { const mapped = Buffer.from([ 1, 2, 3 ]); const definition = {