From 18c7c4cf8d051b4beaad3c75bc52c7bdfdbe2fb6 Mon Sep 17 00:00:00 2001 From: shokuie Date: Mon, 10 Dec 2018 11:38:02 -0800 Subject: [PATCH] Features (#3) * Encoding INTEGER as an unsigned type and applying ASN qualifiers while encoding --- index.js | 27 ++++++++- test/index.js | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index b570c7e..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,12 +74,24 @@ const decoders = { }; const encoders = { - 'INTEGER': (value) => { - const length = (Math.log2(value) >> 3) + 1; + '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 37c60fe..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 = { @@ -107,6 +108,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, @@ -528,6 +544,92 @@ 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 = { + 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 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 = { + 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 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 = { @@ -543,6 +645,64 @@ 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 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 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', + 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 = {