[Pkg-javascript-commits] [node-asn1.js] 24/202: api: partial decode
Bastien Roucariès
rouca at moszumanska.debian.org
Thu Apr 20 19:18:50 UTC 2017
This is an automated email from the git hooks/post-receive script.
rouca pushed a commit to branch master
in repository node-asn1.js.
commit 726e22ca44cd43133dcc00b72606fd6fb4bf3b23
Author: Fedor Indutny <fedor.indutny at gmail.com>
Date: Sat Dec 7 20:53:12 2013 +0400
api: partial decode
---
lib/asn1/api.js | 4 +-
lib/asn1/base/buffer.js | 8 +-
lib/asn1/base/node.js | 31 ++++++--
lib/asn1/base/reporter.js | 30 +++++--
lib/asn1/decoders/der.js | 37 +++++----
test/encode-error-test.js | 82 -------------------
test/error-test.js | 199 ++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 273 insertions(+), 118 deletions(-)
diff --git a/lib/asn1/api.js b/lib/asn1/api.js
index 5812982..7468edc 100644
--- a/lib/asn1/api.js
+++ b/lib/asn1/api.js
@@ -28,11 +28,11 @@ Entity.prototype._createNamed = function createNamed(base) {
return new named(this);
};
-Entity.prototype.decode = function decode(data, enc) {
+Entity.prototype.decode = function decode(data, enc, options) {
// Lazily create decoder
if (!this.decoders.hasOwnProperty(enc))
this.decoders[enc] = this._createNamed(asn1.decoders[enc]);
- return this.decoders[enc].decode(data);
+ return this.decoders[enc].decode(data, options);
};
Entity.prototype.encode = function encode(data, enc, /* internal */ reporter) {
diff --git a/lib/asn1/base/buffer.js b/lib/asn1/base/buffer.js
index 578dbbb..524b545 100644
--- a/lib/asn1/base/buffer.js
+++ b/lib/asn1/base/buffer.js
@@ -36,16 +36,16 @@ DecoderBuffer.prototype.isEmpty = function isEmpty() {
return this.offset === this.length;
};
-DecoderBuffer.prototype.readUInt8 = function readUInt8() {
+DecoderBuffer.prototype.readUInt8 = function readUInt8(fail) {
if (this.offset + 1 <= this.length)
return this.base.readUInt8(this.offset++, true);
else
- return this.error('DecoderBuffer overrun');
+ return this.error(fail || 'DecoderBuffer overrun');
}
-DecoderBuffer.prototype.skip = function skip(bytes) {
+DecoderBuffer.prototype.skip = function skip(bytes, fail) {
if (!(this.offset + bytes <= this.length))
- return this.error('DecoderBuffer overrun');
+ return this.error(fail || 'DecoderBuffer overrun');
var res = new DecoderBuffer(this.base);
diff --git a/lib/asn1/base/node.js b/lib/asn1/base/node.js
index d600d58..cb18394 100644
--- a/lib/asn1/base/node.js
+++ b/lib/asn1/base/node.js
@@ -242,7 +242,7 @@ Node.prototype._decode = function decode(input) {
// Decode root node
if (state.parent === null)
- return state.children[0]._decode(input);
+ return input.wrapResult(state.children[0]._decode(input));
var result = state['default'];
var present = true;
@@ -259,6 +259,8 @@ Node.prototype._decode = function decode(input) {
state.implicit !== null ? state.implicit :
state.tag || 0
);
+ if (input.isError(present))
+ return present;
}
// Push object on stack
@@ -268,16 +270,23 @@ Node.prototype._decode = function decode(input) {
if (present) {
// Unwrap explicit values
- if (state.explicit !== null)
- input = this._decodeTag(input, state.explicit);
+ if (state.explicit !== null) {
+ var explicit = this._decodeTag(input, state.explicit);
+ if (input.isError(explicit))
+ return explicit;
+ input = explicit;
+ }
// Unwrap implicit and normal values
if (state.use === null && !state.any && state.choice === null) {
- input = this._decodeTag(
+ var body = this._decodeTag(
input,
state.implicit !== null ? state.implicit : state.tag,
state.any
);
+ if (input.isError(body))
+ return body;
+ input = body;
}
// Select proper method for tag
@@ -287,12 +296,18 @@ Node.prototype._decode = function decode(input) {
result = this._decodeGeneric(state.tag, input);
else
result = this._decodeChoice(input);
+ if (input.isError(result))
+ return result;
// Decode children
if (!state.any && state.choice === null && state.children !== null) {
- state.children.forEach(function decodeChildren(child) {
+ var fail = state.children.some(function decodeChildren(child) {
+ // NOTE: We are ignoring errors here, to let parser continue with other
+ // parts of encoded data
child._decode(input);
});
+ if (fail)
+ return err;
}
}
@@ -345,7 +360,11 @@ Node.prototype._decodeChoice = function decodeChoice(input) {
var save = input.save();
var node = state.choice[key];
try {
- result = { type: key, value: node._decode(input) };
+ var value = node._decode(input);
+ if (input.isError(value))
+ return false;
+
+ result = { type: key, value: value };
match = true;
} catch (e) {
input.restore(save);
diff --git a/lib/asn1/base/reporter.js b/lib/asn1/base/reporter.js
index 746ccaa..605ae4e 100644
--- a/lib/asn1/base/reporter.js
+++ b/lib/asn1/base/reporter.js
@@ -1,7 +1,5 @@
var util = require('util');
-var errRef = {};
-
function Reporter(options) {
this._reporterState = {
obj: null,
@@ -13,7 +11,7 @@ function Reporter(options) {
exports.Reporter = Reporter;
Reporter.prototype.isError = function isError(obj) {
- return obj === errRef;
+ return obj instanceof ReporterError;
};
Reporter.prototype.enterKey = function enterKey(key) {
@@ -63,13 +61,29 @@ Reporter.prototype.error = function error(msg) {
if (!inherited)
state.errors.push(err);
- return errRef;
+ return err;
};
-function ReporterError(path, msg, stack) {
- var message = 'Error at object path: ' + path + '\n' + msg;
- Error.call(this, message);
+Reporter.prototype.wrapResult = function wrapResult(result) {
+ var state = this._reporterState;
+ if (!state.options.partial)
+ return result;
- this.stack = stack || new Error(message).stack;
+ return {
+ result: this.isError(result) ? null : result,
+ errors: state.errors
+ };
+};
+
+function ReporterError(path, msg) {
+ this.path = path;
+ this.rethrow(msg);
};
util.inherits(ReporterError, Error);
+
+ReporterError.prototype.rethrow = function rethrow(msg) {
+ this.message = msg + ' at: ' + (this.path || '(shallow)');
+ Error.captureStackTrace(this, ReporterError);
+
+ return this;
+};
diff --git a/lib/asn1/decoders/der.js b/lib/asn1/decoders/der.js
index 593eafe..5699c6d 100644
--- a/lib/asn1/decoders/der.js
+++ b/lib/asn1/decoders/der.js
@@ -36,7 +36,7 @@ DERNode.prototype._peekTag = function peekTag(buffer, tag) {
return false;
var state = buffer.save();
- var decodedTag = derDecodeTag(buffer);
+ var decodedTag = derDecodeTag(buffer, 'Failed to peek tag: "' + tag + '"');
if (buffer.isError(decodedTag))
return decodedTag;
@@ -46,11 +46,14 @@ DERNode.prototype._peekTag = function peekTag(buffer, tag) {
};
DERNode.prototype._decodeTag = function decodeTag(buffer, tag, any) {
- var decodedTag = derDecodeTag(buffer);
+ var decodedTag = derDecodeTag(buffer,
+ 'Failed to decode tag of "' + tag + '"');
if (buffer.isError(decodedTag))
return decodedTag;
- var len = derDecodeLen(buffer, decodedTag.primitive);
+ var len = derDecodeLen(buffer,
+ decodedTag.primitive,
+ 'Failed to get length of "' + tag + '"');
// Failure
if (buffer.isError(len))
@@ -60,27 +63,29 @@ DERNode.prototype._decodeTag = function decodeTag(buffer, tag, any) {
decodedTag.tag !== tag &&
decodedTag.tagStr !== tag &&
decodedTag.tagStr + 'of' !== tag) {
- return buffer.error('Failed to match tag: ' + tag);
+ return buffer.error('Failed to match tag: "' + tag + '"');
}
if (decodedTag.primitive || len !== null)
- return buffer.skip(len);
+ return buffer.skip(len, 'Failed to match body of: "' + tag + '"');
// Indefinite length... find END tag
var state = buffer.save();
- var res = this._skipUntilEnd(buffer);
+ var res = this._skipUntilEnd(
+ buffer,
+ 'Failed to skip indefinite length body: "' + this.tag + '"');
if (buffer.isError(res))
return res;
return buffer.restore(state);
};
-DERNode.prototype._skipUntilEnd = function skipUntilEnd(buffer) {
+DERNode.prototype._skipUntilEnd = function skipUntilEnd(buffer, fail) {
while (true) {
- var tag = derDecodeTag(buffer);
+ var tag = derDecodeTag(buffer, fail);
if (buffer.isError(tag))
return tag;
- var len = derDecodeLen(buffer, tag.primitive);
+ var len = derDecodeLen(buffer, tag.primitive, fail);
if (buffer.isError(len))
return len;
@@ -88,7 +93,7 @@ DERNode.prototype._skipUntilEnd = function skipUntilEnd(buffer) {
if (tag.primitive || len !== null)
res = buffer.skip(len)
else
- res = this._skipUntilEnd(buffer);
+ res = this._skipUntilEnd(buffer, fail);
// Failure
if (buffer.isError(res))
@@ -212,8 +217,8 @@ DERNode.prototype._use = function use(buffer, decoder) {
// Utility methods
-function derDecodeTag(buf) {
- var tag = buf.readUInt8();
+function derDecodeTag(buf, fail) {
+ var tag = buf.readUInt8(fail);
if (buf.isError(tag))
return tag;
@@ -225,7 +230,7 @@ function derDecodeTag(buf) {
var oct = tag;
tag = 0;
while ((oct & 0x80) === 0x80) {
- oct = buf.readUInt8();
+ oct = buf.readUInt8(fail);
if (buf.isError(oct))
return oct;
@@ -245,8 +250,8 @@ function derDecodeTag(buf) {
};
}
-function derDecodeLen(buf, primitive) {
- var len = buf.readUInt8();
+function derDecodeLen(buf, primitive, fail) {
+ var len = buf.readUInt8(fail);
if (buf.isError(len))
return len;
@@ -268,7 +273,7 @@ function derDecodeLen(buf, primitive) {
len = 0;
for (var i = 0; i < num; i++) {
len <<= 8;
- var j = buf.readUInt8();
+ var j = buf.readUInt8(fail);
if (buf.isError(j))
return j;
len |= j;
diff --git a/test/encode-error-test.js b/test/encode-error-test.js
deleted file mode 100644
index 7d1c5f8..0000000
--- a/test/encode-error-test.js
+++ /dev/null
@@ -1,82 +0,0 @@
-var assert = require('assert');
-var asn1 = require('..');
-
-var Buffer = require('buffer').Buffer;
-
-describe('asn1.js encode error', function() {
- function test(name, model, input, expected) {
- it('should support ' + name, function() {
- var M = asn1.define('TestModel', model);
-
- var error;
- assert.throws(function() {
- try {
- var encoded = M.encode(input, 'der');
- } catch (e) {
- error = e;
- throw e;
- }
- });
-
- assert(expected.test(error.stack),
- 'Failed to match, expected: ' + expected + ' got: ' +
- JSON.stringify(error.stack));
- });
- }
-
- describe('primitives', function() {
- test('int', function() {
- this.int();
- }, 'hello', /no values map/i);
-
- test('enum', function() {
- this.enum({ 0: 'hello', 1: 'world' });
- }, 'gosh', /contain: "gosh"/);
-
- test('objid', function() {
- this.objid();
- }, 1, /objid\(\) should be either array or string, got: 1/);
- });
-
- describe('composite', function() {
- test('shallow', function() {
- this.seq().obj(
- this.key('key').int()
- );
- }, { key: 'hello' } , /object path: \["key"\]/i);
-
- test('deep and empty', function() {
- this.seq().obj(
- this.key('a').seq().obj(
- this.key('b').seq().obj(
- this.key('c').int()
- )
- )
- );
- }, { } , /object path: \["a"\]\["b"\]/i);
-
- test('deep', function() {
- this.seq().obj(
- this.key('a').seq().obj(
- this.key('b').seq().obj(
- this.key('c').int()
- )
- )
- );
- }, { a: { b: { c: 'hello' } } } , /object path: \["a"\]\["b"\]\["c"\]/i);
-
- test('use', function() {
- var S = asn1.define('S', function() {
- this.seq().obj(
- this.key('x').int()
- );
- });
-
- this.seq().obj(
- this.key('a').seq().obj(
- this.key('b').use(S)
- )
- );
- }, { a: { b: { x: 'hello' } } } , /object path: \["a"\]\["b"\]\["x"\]/i);
- });
-});
diff --git a/test/error-test.js b/test/error-test.js
new file mode 100644
index 0000000..3535482
--- /dev/null
+++ b/test/error-test.js
@@ -0,0 +1,199 @@
+var assert = require('assert');
+var asn1 = require('..');
+
+var Buffer = require('buffer').Buffer;
+
+describe('asn1.js error', function() {
+ describe('encoder', function() {
+ function test(name, model, input, expected) {
+ it('should support ' + name, function() {
+ var M = asn1.define('TestModel', model);
+
+ var error;
+ assert.throws(function() {
+ try {
+ var encoded = M.encode(input, 'der');
+ } catch (e) {
+ error = e;
+ throw e;
+ }
+ });
+
+ assert(expected.test(error.stack),
+ 'Failed to match, expected: ' + expected + ' got: ' +
+ JSON.stringify(error.stack));
+ });
+ }
+
+ describe('primitives', function() {
+ test('int', function() {
+ this.int();
+ }, 'hello', /no values map/i);
+
+ test('enum', function() {
+ this.enum({ 0: 'hello', 1: 'world' });
+ }, 'gosh', /contain: "gosh"/);
+
+ test('objid', function() {
+ this.objid();
+ }, 1, /objid\(\) should be either array or string, got: 1/);
+ });
+
+ describe('composite', function() {
+ test('shallow', function() {
+ this.seq().obj(
+ this.key('key').int()
+ );
+ }, { key: 'hello' } , /map at: \["key"\]/i);
+
+ test('deep and empty', function() {
+ this.seq().obj(
+ this.key('a').seq().obj(
+ this.key('b').seq().obj(
+ this.key('c').int()
+ )
+ )
+ );
+ }, { } , /input is not object at: \["a"\]\["b"\]/i);
+
+ test('deep', function() {
+ this.seq().obj(
+ this.key('a').seq().obj(
+ this.key('b').seq().obj(
+ this.key('c').int()
+ )
+ )
+ );
+ }, { a: { b: { c: 'hello' } } } , /map at: \["a"\]\["b"\]\["c"\]/i);
+
+ test('use', function() {
+ var S = asn1.define('S', function() {
+ this.seq().obj(
+ this.key('x').int()
+ );
+ });
+
+ this.seq().obj(
+ this.key('a').seq().obj(
+ this.key('b').use(S)
+ )
+ );
+ }, { a: { b: { x: 'hello' } } } , /map at: \["a"\]\["b"\]\["x"\]/i);
+ });
+ });
+
+ describe('decoder', function() {
+ function test(name, model, input, expected) {
+ it('should support ' + name, function() {
+ var M = asn1.define('TestModel', model);
+
+ var error;
+ assert.throws(function() {
+ try {
+ var decoded = M.decode(new Buffer(input, 'hex'), 'der');
+ } catch (e) {
+ error = e;
+ throw e;
+ }
+ });
+ var partial = M.decode(new Buffer(input, 'hex'), 'der', {
+ partial: true
+ });
+
+ assert(expected.test(error.stack),
+ 'Failed to match, expected: ' + expected + ' got: ' +
+ JSON.stringify(error.stack));
+
+ assert.equal(partial.errors.length, 1);
+ assert(expected.test(partial.errors[0].stack),
+ 'Failed to match, expected: ' + expected + ' got: ' +
+ JSON.stringify(partial.errors[0].stack));
+ });
+ }
+
+ describe('primitive', function() {
+ test('int', function() {
+ this.int();
+ }, '2201', /body of: "int"/);
+
+ test('int', function() {
+ this.int();
+ }, '', /tag of "int"/);
+ });
+
+ describe('composite', function() {
+ test('shallow', function() {
+ this.seq().obj(
+ this.key('a').seq().obj()
+ );
+ }, '30', /length of "seq"/);
+
+ test('deep and empty', function() {
+ this.seq().obj(
+ this.key('a').seq().obj(
+ this.key('b').seq().obj(
+ this.key('c').int()
+ )
+ )
+ );
+ }, '300430023000', /tag of "int" at: \["a"\]\["b"\]\["c"\]/);
+
+ test('deep and incomplete', function() {
+ this.seq().obj(
+ this.key('a').seq().obj(
+ this.key('b').seq().obj(
+ this.key('c').int()
+ )
+ )
+ );
+ }, '30053003300122', /length of "int" at: \["a"\]\["b"\]\["c"\]/);
+ });
+ });
+
+ describe('partial decoder', function() {
+ function test(name, model, input, expectedObj, expectedErrs) {
+ it('should support ' + name, function() {
+ var M = asn1.define('TestModel', model);
+
+ var decoded = M.decode(new Buffer(input, 'hex'), 'der', {
+ partial: true
+ });
+
+ assert.deepEqual(decoded.result, expectedObj);
+
+ assert.equal(decoded.errors.length, expectedErrs.length);
+ expectedErrs.forEach(function(expected, i) {
+ assert(expected.test(decoded.errors[i].stack),
+ 'Failed to match, expected: ' + expected + ' got: ' +
+ JSON.stringify(decoded.errors[i].stack));
+ });
+ });
+ }
+
+ test('last key not present', function() {
+ this.seq().obj(
+ this.key('a').seq().obj(
+ this.key('b').seq().obj(
+ this.key('c').int()
+ ),
+ this.key('d').int()
+ )
+ );
+ }, '30073005300022012e', { a: { b: {}, d: 46 } }, [
+ /"int" at: \["a"\]\["b"\]\["c"\]/
+ ]);
+
+ test('first key not present', function() {
+ this.seq().obj(
+ this.key('a').seq().obj(
+ this.key('b').seq().obj(
+ this.key('c').int()
+ ),
+ this.key('d').int()
+ )
+ );
+ }, '30073005300322012e', { a: { b: { c: 46 } } }, [
+ /"int" at: \["a"\]\["d"\]/
+ ]);
+ });
+});
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-asn1.js.git
More information about the Pkg-javascript-commits
mailing list