Skip to content

Instantly share code, notes, and snippets.

@yurydelendik
Created August 13, 2014 00:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yurydelendik/89a046484dcf02a3013e to your computer and use it in GitHub Desktop.
Save yurydelendik/89a046484dcf02a3013e to your computer and use it in GitHub Desktop.
// http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
var UNIVERSAL_TAG_CLASS = 0;
var EOC_TAG = 0;
var BOOLEAN_TAG = 1;
var INTEGER_TAG = 2;
var NULL_TAG = 5;
var OBJECT_ID_TAG = 6;
var RELATIVE_OBJECT_ID_TAG = 13;
var PRINTABLE_STRING_TAG = 19;
var IA5_STRING_TAG = 22;
var UTC_TIME_TAG = 23;
function readBerPrimitive(tagClass, tagNumber, data, start, end) {
if (tagClass === UNIVERSAL_TAG_CLASS) {
switch (tagNumber) {
case EOC_TAG:
if (start !== end) {
throw new Error('Invalid EOC tag');
}
return undefined;
case BOOLEAN_TAG:
if (start + 1 !== end) {
throw new Error('Invalid boolean tag');
}
return !!data[start];
case NULL_TAG:
if (start !== end) {
throw new Error('Invalid null tag');
}
return null;
case INTEGER_TAG:
if (start === end) {
throw new Error('Invalid integer tag');
} else if (start + 1 === end) {
return (data[start] << 24) >> 24;
} else if (start + 2 === end) {
return ((data[start] << 24) | (data[start + 1] << 16)) >> 16;
} else if (start + 3 === end) {
return ((data[start] << 24) | (data[start + 1] << 16) |
(data[start + 2] << 8)) >> 8;
} else if (start + 4 === end) {
return (data[start] << 24) | (data[start + 1] << 16) |
(data[start + 2] << 8) | data[start + 3];
}
// to long for us
break;
case OBJECT_ID_TAG:
case RELATIVE_OBJECT_ID_TAG:
if (start === end) {
throw new Error('Invalid identifier tag');
}
var id = [];
for (var i = start; i < end; ) {
var tmp = decodeCompressedNumber(data, i, end);
id.push(tmp.value);
i += tmp.octetLength;
}
if (tagNumber === OBJECT_ID_TAG) {
var firstNumber = +id.shift();
id.unshift(((firstNumber / 40) | 0).toString(),
(firstNumber % 40).toString());
}
return id.join('.');
case PRINTABLE_STRING_TAG:
case IA5_STRING_TAG:
case UTC_TIME_TAG:
return String.fromCharCode.apply(null, data.subarray(start, end));
}
}
return data.subarray(start, end);
}
function decodeCompressedNumber(data, start, end) {
var j = start, k;
while (j < end && (data[j] & 0x80)) {
j++;
}
if (j >= end) {
throw new Error('Invalid identifier bytes encoding');
}
var str = '', mult = 128, buffer = data[j];
k = j;
var partSize = 10000;
while (start < k) {
buffer += (data[--k] & 0x7F) * mult;
mult <<= 7;
if (mult >= partSize) {
str = ((buffer % partSize) + partSize).toString().substr(1) + str;
mult = (mult / partSize) | 0;
buffer = (buffer / partSize) | 0;
}
}
if (buffer !== 0) {
str = buffer.toString() + str;
} else {
while (str.length > 1 && str[0] === '0') {
str = str.substr(1);
}
}
return {
value: str,
octetLength: j - start + 1
};
}
function readBer(data, start, end) {
var i = start, k;
// 8.1.2 Identifier octets
var tagOctet = data[i++];
var tagNumber = tagOctet & 0x1F;
if (tagNumber === 0x1F) {
// 8.1.2.4
var tmpNumber = decodeCompressedNumber(data, i, end);
tagNumber = tmpNumber.value;
i += tmpNumber.octetLength;
}
var tagClass = tagOctet >> 6;
var constructed = !!(tagOctet & 0x20); // 8.1.2.3
var contents;
var b = data[i];
if (b === 0x80) {
// 8.1.3.6 indefinite form
if (!constructed) {
throw new Error('primitive cannot have indefinite form length');
}
i++;
contents = [];
var eocFound = false;
while (i < end) {
child = readBer(data, i, end);
i += child.length;
if (child.class === UNIVERSAL_TAG_CLASS && child.number === EOC_TAG) {
eocFound = true; // end-of-contents octets
break;
}
contents.push(child);
}
if (!eocFound) {
throw new Error('EOC is not found');
}
} else {
// 8.1.3.3
var shortForm = !(b & 0x80);
var length;
if (shortForm) {
length = b;
i++;
} else {
// 8.1.3.5
var lengthOfLength = b & 0x7F;
if (lengthOfLength >= 4) {
throw new Error('length is too long');
}
i++;
length = 0;
for (k = 0; k < lengthOfLength; k++) {
length = (length << 8) | data[i++];
}
length = length >>> 0;
}
if (i + length > end) {
throw new Error('data tag overflow');
}
if (!constructed) {
contents = readBerPrimitive(tagClass, tagNumber, data, i, i + length);
i += length;
} else {
contents = [];
while (i < end) {
child = readBer(data, i, end);
i += child.length;
contents.push(child);
}
}
}
return {
class: tagClass,
number: tagNumber,
constructed: constructed,
contents: contents,
offset: start,
length: i - start
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment