natuscommodi
Version:
ASN.1 encoding and decoding
1,186 lines (1,167 loc) • 90.4 kB
JavaScript
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ var threw = true;
/******/ try {
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ threw = false;
/******/ } finally {
/******/ if(threw) delete installedModules[moduleId];
/******/ }
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(1);
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
// CONCATENATED MODULE: ./source/errors.ts
class ASN1Error extends Error {
constructor(m) {
super(m);
Object.setPrototypeOf(this, ASN1Error.prototype);
}
}
class ASN1NotImplementedError extends ASN1Error {
constructor() {
super("Not yet implemented.");
Object.setPrototypeOf(this, ASN1Error.prototype);
}
}
class ASN1RecursionError extends ASN1Error {
constructor() {
super("Recursion was too deep.");
Object.setPrototypeOf(this, ASN1Error.prototype);
}
}
class ASN1TruncationError extends ASN1Error {
constructor(m) {
super(m);
Object.setPrototypeOf(this, ASN1Error.prototype);
}
}
class ASN1OverflowError extends ASN1Error {
constructor(m) {
super(m);
Object.setPrototypeOf(this, ASN1Error.prototype);
}
}
class ASN1SizeError extends ASN1Error {
constructor(m) {
super(m);
Object.setPrototypeOf(this, ASN1Error.prototype);
}
}
class ASN1PaddingError extends ASN1Error {
constructor(m) {
super(m);
Object.setPrototypeOf(this, ASN1Error.prototype);
}
}
class ASN1UndefinedError extends ASN1Error {
constructor(m) {
super(m);
Object.setPrototypeOf(this, ASN1Error.prototype);
}
}
class ASN1CharactersError extends ASN1Error {
constructor(m) {
super(m);
Object.setPrototypeOf(this, ASN1Error.prototype);
}
}
class ASN1ConstructionError extends ASN1Error {
constructor(m) {
super(m);
Object.setPrototypeOf(this, ASN1Error.prototype);
}
}
// CONCATENATED MODULE: ./source/asn1.ts
class asn1_ASN1Element {
constructor() {
this.recursionCount = 0;
this.tagClass = 0;
this.construction = 0;
this.tagNumber = 0;
this.value = new Uint8Array(0);
}
get length() {
return this.value.length;
}
static validateDateTime(dataType, year, month, date, hours, minutes, seconds) {
switch (month) {
case 0:
case 2:
case 4:
case 6:
case 7:
case 9:
case 11:
if (date > 31)
throw new ASN1Error(`Day > 31 encountered in ${dataType} with 31-day month.`);
break;
case 3:
case 5:
case 8:
case 10:
if (date > 30)
throw new ASN1Error(`Day > 31 encountered in ${dataType} with 30-day month.`);
break;
case 1:
let isLeapYear = ((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0);
if (isLeapYear) {
if (date > 29)
throw new ASN1Error(`Day > 29 encountered in ${dataType} with month of February in leap year.`);
}
else {
if (date > 28)
throw new ASN1Error(`Day > 28 encountered in ${dataType} with month of February and non leap year.`);
}
break;
default:
throw new ASN1Error(`Month greater than 12 encountered in ${dataType}.`);
}
if (hours > 23)
throw new ASN1Error(`Hours > 23 encountered in ${dataType}.`);
if (minutes > 59)
throw new ASN1Error(`Minutes > 60 encountered in ${dataType}.`);
if (seconds > 59)
throw new ASN1Error(`Seconds > 60 encountered in ${dataType}.`);
}
static decodeUnsignedBigEndianInteger(value) {
if (value.length === 0)
return 0;
if (value.length > 4)
throw new ASN1OverflowError("Number too long to decode.");
const u8 = new Uint8Array(4);
u8.set(value, (4 - value.length));
return new Uint32Array(u8.reverse().buffer)[0];
}
static decodeSignedBigEndianInteger(value) {
if (value.length === 0)
return 0;
if (value.length > 4)
throw new ASN1OverflowError("Number too long to decode.");
const u8 = new Uint8Array(4);
if (value[0] >= 0b10000000)
u8.fill(0xFF);
u8.set(value, (4 - value.length));
return new Int32Array(u8.reverse().buffer)[0];
}
}
asn1_ASN1Element.nestingRecursionLimit = 5;
// CONCATENATED MODULE: ./source/values.ts
const MAX_UINT_32 = 0x00FFFFFFFF;
const MIN_UINT_32 = 0x0000000000;
const MAX_SINT_32 = 0x7FFFFFFF;
const MIN_SINT_32 = -0x80000000;
var ASN1TagClass;
(function (ASN1TagClass) {
ASN1TagClass[ASN1TagClass["universal"] = 0] = "universal";
ASN1TagClass[ASN1TagClass["application"] = 1] = "application";
ASN1TagClass[ASN1TagClass["context"] = 3] = "context";
ASN1TagClass[ASN1TagClass["private"] = 4] = "private";
})(ASN1TagClass || (ASN1TagClass = {}));
var ASN1Construction;
(function (ASN1Construction) {
ASN1Construction[ASN1Construction["primitive"] = 0] = "primitive";
ASN1Construction[ASN1Construction["constructed"] = 1] = "constructed";
})(ASN1Construction || (ASN1Construction = {}));
var LengthEncodingPreference;
(function (LengthEncodingPreference) {
LengthEncodingPreference[LengthEncodingPreference["definite"] = 0] = "definite";
LengthEncodingPreference[LengthEncodingPreference["indefinite"] = 1] = "indefinite";
})(LengthEncodingPreference || (LengthEncodingPreference = {}));
var ASN1SpecialRealValue;
(function (ASN1SpecialRealValue) {
ASN1SpecialRealValue[ASN1SpecialRealValue["plusInfinity"] = 64] = "plusInfinity";
ASN1SpecialRealValue[ASN1SpecialRealValue["minusInfinity"] = 65] = "minusInfinity";
ASN1SpecialRealValue[ASN1SpecialRealValue["notANumber"] = 66] = "notANumber";
ASN1SpecialRealValue[ASN1SpecialRealValue["minusZero"] = 67] = "minusZero";
})(ASN1SpecialRealValue || (ASN1SpecialRealValue = {}));
var ASN1RealEncodingBase;
(function (ASN1RealEncodingBase) {
ASN1RealEncodingBase[ASN1RealEncodingBase["base2"] = 0] = "base2";
ASN1RealEncodingBase[ASN1RealEncodingBase["base8"] = 16] = "base8";
ASN1RealEncodingBase[ASN1RealEncodingBase["base16"] = 32] = "base16";
})(ASN1RealEncodingBase || (ASN1RealEncodingBase = {}));
var ASN1RealEncodingScale;
(function (ASN1RealEncodingScale) {
ASN1RealEncodingScale[ASN1RealEncodingScale["scale0"] = 0] = "scale0";
ASN1RealEncodingScale[ASN1RealEncodingScale["scale1"] = 4] = "scale1";
ASN1RealEncodingScale[ASN1RealEncodingScale["scale2"] = 8] = "scale2";
ASN1RealEncodingScale[ASN1RealEncodingScale["scale3"] = 12] = "scale3";
})(ASN1RealEncodingScale || (ASN1RealEncodingScale = {}));
var ASN1UniversalType;
(function (ASN1UniversalType) {
ASN1UniversalType[ASN1UniversalType["endOfContent"] = 0] = "endOfContent";
ASN1UniversalType[ASN1UniversalType["boolean"] = 1] = "boolean";
ASN1UniversalType[ASN1UniversalType["integer"] = 2] = "integer";
ASN1UniversalType[ASN1UniversalType["bitString"] = 3] = "bitString";
ASN1UniversalType[ASN1UniversalType["octetString"] = 4] = "octetString";
ASN1UniversalType[ASN1UniversalType["nill"] = 5] = "nill";
ASN1UniversalType[ASN1UniversalType["objectIdentifier"] = 6] = "objectIdentifier";
ASN1UniversalType[ASN1UniversalType["objectDescriptor"] = 7] = "objectDescriptor";
ASN1UniversalType[ASN1UniversalType["external"] = 8] = "external";
ASN1UniversalType[ASN1UniversalType["realNumber"] = 9] = "realNumber";
ASN1UniversalType[ASN1UniversalType["enumerated"] = 10] = "enumerated";
ASN1UniversalType[ASN1UniversalType["embeddedPDV"] = 11] = "embeddedPDV";
ASN1UniversalType[ASN1UniversalType["utf8String"] = 12] = "utf8String";
ASN1UniversalType[ASN1UniversalType["relativeOID"] = 13] = "relativeOID";
ASN1UniversalType[ASN1UniversalType["reserved14"] = 14] = "reserved14";
ASN1UniversalType[ASN1UniversalType["reserved15"] = 15] = "reserved15";
ASN1UniversalType[ASN1UniversalType["sequence"] = 16] = "sequence";
ASN1UniversalType[ASN1UniversalType["set"] = 17] = "set";
ASN1UniversalType[ASN1UniversalType["numericString"] = 18] = "numericString";
ASN1UniversalType[ASN1UniversalType["printableString"] = 19] = "printableString";
ASN1UniversalType[ASN1UniversalType["teletexString"] = 20] = "teletexString";
ASN1UniversalType[ASN1UniversalType["videotexString"] = 21] = "videotexString";
ASN1UniversalType[ASN1UniversalType["ia5String"] = 22] = "ia5String";
ASN1UniversalType[ASN1UniversalType["utcTime"] = 23] = "utcTime";
ASN1UniversalType[ASN1UniversalType["generalizedTime"] = 24] = "generalizedTime";
ASN1UniversalType[ASN1UniversalType["graphicString"] = 25] = "graphicString";
ASN1UniversalType[ASN1UniversalType["visibleString"] = 26] = "visibleString";
ASN1UniversalType[ASN1UniversalType["generalString"] = 27] = "generalString";
ASN1UniversalType[ASN1UniversalType["universalString"] = 28] = "universalString";
ASN1UniversalType[ASN1UniversalType["characterString"] = 29] = "characterString";
ASN1UniversalType[ASN1UniversalType["bmpString"] = 30] = "bmpString";
})(ASN1UniversalType || (ASN1UniversalType = {}));
const printableStringCharacters = "etaoinsrhdlucmfywgpbvkxqjzETAOINSRHDLUCMFYWGPBVKXQJZ0123456789 '()+,-./:=?";
const utcTimeRegex = /^(?<year>\d{2})(?<month>(?:1[0-2])|(?:0\d))(?<date>(?:3[01])|(?:[0-2]\d))(?<hour>(?:2[0-3])|(?:[01]\d))(?<minute>[0-5]\d)(?<second>[0-5]\d)?(?<offset>(?:(\+|\-)((?:2[0-3])|(?:[01]\d))[0-5]\d)|Z)$/;
const distinguishedUTCTimeRegex = /^(?<year>\d{2})(?<month>(?:1[0-2])|(?:0\d))(?<date>(?:3[01])|(?:[0-2]\d))(?<hour>(?:2[0-3])|(?:[01]\d))(?<minute>[0-5]\d)(?<second>[0-5]\d)?Z$/;
const generalizedTimeRegex = /^(?<year>\d{4})(?<month>(?:1[0-2])|(?:0\d))(?<date>(?:3[01])|(?:[0-2]\d))(?<hour>(?:2[0-3])|(?:[01]\d))(?<minute>[0-5]\d)?(?<second>[0-5]\d)?(?:(\.|,)(?<fraction>\d+))?(?<offset>(?:(\+|\-)((?:2[0-3])|(?:[01]\d))[0-5]\d)|Z)?$/;
const distinguishedGeneralizedTimeRegex = /^(?<year>\d{4})(?<month>(?:1[0-2])|(?:0\d))(?<date>(?:3[01])|(?:[0-2]\d))(?<hour>(?:2[0-3])|(?:[01]\d))(?<minute>[0-5]\d)(?<second>[0-5]\d)(?:(\.|,)(?<fraction>\d*[1-9]))?Z$/;
const nr1Regex = /^\ *(\+|\-)?\d+$/;
const nr2Regex = /^\ *(\+|\-)?(?:\d+(\.|,)\d*)|(?:\d*(\.|,)\d+)$/;
const nr3Regex = /^\ *(\+|\-)?(?:\d+(\.|,)\d*)|(?:\d*(\.|,)\d+)(e|E)(\+|\-)?\d+$/;
const canonicalNR3Regex = /^\ *\-?(?:[1-9]\d*)?[1-9]\.E(?:\+0)|(?:\-?[1-9]\d*)$/;
const distinguishedNR3Regex = /^\ *\-?(?:[1-9]\d*)?[1-9]\.E(?:\+0)|(?:\-?[1-9]\d*)$/;
// CONCATENATED MODULE: ./source/types/objectidentifier.ts
class ObjectIdentifier {
constructor(nodes) {
if (nodes.length < 2)
throw new Error("Cannot construct an OID with less than two nodes!");
if (nodes.length >= 1 && !(nodes[0] in [0, 1, 2]))
throw new Error("OIDs first node must be 0, 1, or 2!");
if (((nodes[0] < 2) && nodes[1] > 39) ||
(nodes[0] === 2 && nodes[1] > 175))
throw new Error(`OID Node #2 cannot exceed 39 if node #1 is 0 or 1, and 175 if node #1 is 2. Received these nodes: ${nodes}.`);
nodes.forEach(node => {
if (node < 0)
throw new Error("OID node numbers cannot be negative!");
if (node > Number.MAX_SAFE_INTEGER)
throw new Error("OID number was too big!");
});
this._nodes = nodes.slice(0);
}
get nodes() {
return this._nodes.slice(0);
}
get dotDelimitedNotation() {
return this._nodes.join(".");
}
toString() {
return this.dotDelimitedNotation;
}
}
// CONCATENATED MODULE: ./source/x690.ts
class x690_X690Element extends asn1_ASN1Element {
validateTag(permittedClasses, permittedConstruction, permittedNumbers) {
if (!permittedClasses.includes(this.tagClass))
return -1;
if (!permittedConstruction.includes(this.construction))
return -2;
if (!permittedNumbers.includes(this.tagNumber))
return -3;
return 0;
}
set integer(value) {
if (value < MIN_SINT_32)
throw new ASN1OverflowError(`Number ${value} too small to be converted.`);
if (value > MAX_SINT_32)
throw new ASN1OverflowError(`Number ${value} too big to be converted.`);
if (value <= 127 && value >= -128) {
this.value = new Uint8Array([
(value & 255)
]);
return;
}
else if (value <= 32767 && value >= -32768) {
this.value = new Uint8Array([
(value >> 8 & 255),
(value & 255)
]);
return;
}
else if (value <= 8388607 && value >= -8388608) {
this.value = new Uint8Array([
((value >> 16) & 255),
(value >> 8 & 255),
(value & 255)
]);
return;
}
else {
this.value = new Uint8Array([
((value >> 24) & 255),
((value >> 16) & 255),
(value >> 8 & 255),
(value & 255)
]);
return;
}
}
get integer() {
if (this.construction !== 0)
throw new ASN1ConstructionError("INTEGER cannot be constructed.");
if (this.value.length === 0)
throw new ASN1SizeError("Number encoded on zero bytes!");
if (this.value.length > 4)
throw new ASN1OverflowError("Number too long to decode.");
if (this.value.length > 2 &&
((this.value[0] === 0xFF && this.value[1] >= 0b10000000) ||
(this.value[0] === 0x00 && this.value[1] < 0b10000000)))
throw new ASN1PaddingError("Unnecessary padding bytes on INTEGER or ENUMERATED.");
return asn1_ASN1Element.decodeSignedBigEndianInteger(this.value.subarray(0));
}
set objectIdentifier(value) {
const numbers = value.nodes;
let pre = [((numbers[0] * 40) + numbers[1])];
let post = x690_X690Element.encodeObjectIdentifierNodes(numbers.slice(2));
this.value = new Uint8Array(pre.concat(post));
}
get objectIdentifier() {
if (this.construction !== 0)
throw new ASN1ConstructionError("OBJECT IDENTIFIER cannot be constructed.");
if (this.value.length === 0)
throw new ASN1TruncationError("Encoded value was too short to be an OBJECT IDENTIFIER!");
let numbers = [0, 0];
if (this.value[0] >= 0x50) {
numbers[0] = 2;
numbers[1] = (this.value[0] - 0x50);
}
else if (this.value[0] >= 0x28) {
numbers[0] = 1;
numbers[1] = (this.value[0] - 0x28);
}
else {
numbers[0] = 0;
numbers[1] = this.value[0];
}
if (this.value.length === 1)
return new ObjectIdentifier(numbers);
numbers = numbers.concat(x690_X690Element.decodeObjectIdentifierNodes(this.value.slice(1)));
return new ObjectIdentifier(numbers);
}
set enumerated(value) {
this.integer = value;
}
get enumerated() {
return this.integer;
}
set relativeObjectIdentifier(value) {
this.value = new Uint8Array(x690_X690Element.encodeObjectIdentifierNodes(value));
}
get relativeObjectIdentifier() {
if (this.construction !== 0)
throw new ASN1ConstructionError("Relative OID cannot be constructed.");
return x690_X690Element.decodeObjectIdentifierNodes(this.value.slice(0));
}
static decodeObjectIdentifierNodes(value) {
if (value.length === 0)
return [];
let numbers = [];
if (value.length > 0 && (value[(value.length - 1)] & 0b10000000) === 0b10000000)
throw new ASN1TruncationError("OID truncated");
let components = 0;
value.forEach(b => { if (!(b & 0b10000000))
components++; });
numbers.length = components;
let currentNumber = 0;
let bytesUsedInCurrentNumber = 0;
value.forEach(b => {
if (bytesUsedInCurrentNumber === 0 && b === 0b10000000)
throw new ASN1PaddingError("OID had invalid padding byte.");
if (numbers[currentNumber] > (Number.MAX_SAFE_INTEGER >>> 7))
throw new ASN1OverflowError("OID node too big");
numbers[currentNumber] <<= 7;
numbers[currentNumber] |= (b & 0x7F);
if (!(b & 0b10000000)) {
currentNumber++;
bytesUsedInCurrentNumber = 0;
}
else {
bytesUsedInCurrentNumber++;
}
});
return numbers;
}
static encodeObjectIdentifierNodes(value) {
let ret = [];
for (let i = 0; i < value.length; i++) {
let number = value[i];
if (number < 128) {
ret.push(number);
continue;
}
let encodedOIDNode = [];
while (number !== 0) {
let numberBytes = [
(number & 255),
(number >>> 8 & 255),
((number >>> 16) & 255),
((number >>> 24) & 255),
];
if ((numberBytes[0] & 0x80) === 0)
numberBytes[0] |= 0x80;
encodedOIDNode.unshift(numberBytes[0]);
number >>= 7;
}
encodedOIDNode[encodedOIDNode.length - 1] &= 0x7F;
ret = ret.concat(encodedOIDNode);
}
return ret;
}
}
// CONCATENATED MODULE: ./source/codecs/ber.ts
class ber_BERElement extends x690_X690Element {
constructor(tagClass = 0, construction = 0, tagNumber = 0) {
super();
this.tagClass = tagClass;
this.construction = construction;
this.tagNumber = tagNumber;
this.value = new Uint8Array(0);
}
set boolean(value) {
this.value = new Uint8Array(1);
this.value[0] = (value ? 0xFF : 0x00);
}
get boolean() {
if (this.construction !== 0)
throw new ASN1ConstructionError("BOOLEAN cannot be constructed.");
if (this.value.length !== 1)
throw new ASN1SizeError("BOOLEAN not one byte");
return (this.value[0] !== 0);
}
set bitString(value) {
if (value.length === 0)
this.value = new Uint8Array(0);
let pre = [];
pre.length = ((value.length >>> 3) + ((value.length % 8) ? 1 : 0)) + 1;
for (let i = 0; i < value.length; i++) {
if (value[i] === false)
continue;
pre[((i >>> 3) + 1)] |= (0b10000000 >>> (i % 8));
}
pre[0] = (8 - (value.length % 8));
if (pre[0] === 8)
pre[0] = 0;
this.value = new Uint8Array(pre);
}
get bitString() {
if (this.construction === 0) {
if (this.value.length === 0)
throw new ASN1Error("ASN.1 BIT STRING cannot be encoded on zero bytes!");
if (this.value.length === 1 && this.value[0] !== 0)
throw new ASN1Error("ASN.1 BIT STRING encoded with deceptive first byte!");
if (this.value[0] > 7)
throw new ASN1Error("First byte of an ASN.1 BIT STRING must be <= 7!");
let ret = [];
for (let i = 1; i < this.value.length; i++) {
ret = ret.concat([
(this.value[i] & 0b10000000 ? true : false),
(this.value[i] & 0b01000000 ? true : false),
(this.value[i] & 0b00100000 ? true : false),
(this.value[i] & 0b00010000 ? true : false),
(this.value[i] & 0b00001000 ? true : false),
(this.value[i] & 0b00000100 ? true : false),
(this.value[i] & 0b00000010 ? true : false),
(this.value[i] & 0b00000001 ? true : false)
]);
}
ret.length -= this.value[0];
return ret;
}
else {
if ((this.recursionCount + 1) > ber_BERElement.nestingRecursionLimit)
throw new ASN1RecursionError();
let appendy = [];
const substrings = this.sequence;
substrings.slice(0, (substrings.length - 1)).forEach(substring => {
if (substring.construction === 0 &&
substring.value.length > 0 &&
substring.value[0] !== 0x00)
throw new ASN1Error(`Only the last subelement of a constructed BIT STRING may have a non-zero first value byte.`);
});
substrings.forEach(substring => {
if (substring.tagClass !== this.tagClass)
throw new ASN1ConstructionError(`Invalid tag class in recursively-encoded BIT STRING.`);
if (substring.tagNumber !== this.tagNumber)
throw new ASN1ConstructionError(`Invalid tag class in recursively-encoded BIT STRING.`);
substring.recursionCount = (this.recursionCount + 1);
appendy = appendy.concat(substring.bitString);
});
return appendy;
}
}
set octetString(value) {
this.value = value.subarray(0);
}
get octetString() {
return this.deconstruct("OCTET STRING");
}
set objectDescriptor(value) {
this.graphicString = value;
}
get objectDescriptor() {
return this.graphicString;
}
set real(value) {
if (value === 0.0) {
this.value = new Uint8Array(0);
return;
}
else if (isNaN(value)) {
this.value = new Uint8Array([66]);
return;
}
else if (value === -0.0) {
this.value = new Uint8Array([67]);
return;
}
else if (value === Infinity) {
this.value = new Uint8Array([64]);
return;
}
else if (value === -Infinity) {
this.value = new Uint8Array([65]);
return;
}
let valueString = value.toFixed(7);
valueString = (String.fromCharCode(0b00000011) + valueString);
this.value = (new TextEncoder()).encode(valueString);
}
get real() {
if (this.construction !== 0)
throw new ASN1ConstructionError("REAL cannot be constructed.");
if (this.value.length === 0)
return 0.0;
switch (this.value[0] & 0b11000000) {
case (0b01000000): {
if (this.value[0] === 66)
return NaN;
if (this.value[0] === 67)
return -0.0;
if (this.value[0] === 64)
return Infinity;
if (this.value[0] === 65)
return -Infinity;
throw new ASN1UndefinedError("Unrecognized special REAL value!");
}
case (0b00000000): {
let realString;
if (typeof TextEncoder !== "undefined") {
realString = (new TextDecoder("utf-8")).decode(this.value.slice(1));
}
else if (typeof Buffer !== "undefined") {
realString = (new Buffer(this.value.slice(1))).toString("utf-8");
}
else {
throw new ASN1Error("No ability to decode bytes to string!");
}
switch (this.value[0] & 0b00111111) {
case 1: {
if (!nr1Regex.test(realString))
throw new ASN1Error("Malformed NR1 Base-10 REAL");
return parseFloat(realString);
}
case 2: {
if (!nr2Regex.test(realString))
throw new ASN1Error("Malformed NR2 Base-10 REAL");
return parseFloat(realString.replace(",", "."));
}
case 3: {
if (!nr3Regex.test(realString))
throw new ASN1Error("Malformed NR3 Base-10 REAL");
return parseFloat(realString.replace(",", "."));
}
default:
throw new ASN1UndefinedError("Undefined Base-10 REAL encoding.");
}
}
case (0b10000000):
case (0b11000000): {
const sign = ((this.value[0] & 0b01000000) ? -1 : 1);
const base = ((flag) => {
switch (flag) {
case (0): return 2;
case (16): return 8;
case (32): return 16;
default:
throw new ASN1Error("Impossible REAL encoding base encountered.");
}
})(this.value[0] & 0b00110000);
const scale = ((flag) => {
switch (flag) {
case (0): return 0;
case (4): return 1;
case (8): return 2;
case (12): return 3;
default:
throw new ASN1Error("Impossible REAL encoding scale encountered.");
}
})(this.value[0] & 0b00001100);
let exponent;
let mantissa;
switch (this.value[0] & 0b00000011) {
case (0b00000000): {
if (this.value.length < 3)
throw new ASN1TruncationError("Binary-encoded REAL truncated.");
exponent = asn1_ASN1Element.decodeSignedBigEndianInteger(this.value.subarray(1, 2));
mantissa = asn1_ASN1Element.decodeUnsignedBigEndianInteger(this.value.subarray(2));
break;
}
case (0b00000001): {
if (this.value.length < 4)
throw new ASN1TruncationError("Binary-encoded REAL truncated.");
exponent = asn1_ASN1Element.decodeSignedBigEndianInteger(this.value.subarray(1, 3));
mantissa = asn1_ASN1Element.decodeUnsignedBigEndianInteger(this.value.subarray(3));
break;
}
case (0b00000010): {
if (this.value.length < 5)
throw new ASN1TruncationError("Binary-encoded REAL truncated.");
exponent = asn1_ASN1Element.decodeSignedBigEndianInteger(this.value.subarray(1, 4));
mantissa = asn1_ASN1Element.decodeUnsignedBigEndianInteger(this.value.subarray(4));
break;
}
case (0b00000011): {
if (this.value.length < 3)
throw new ASN1TruncationError("Binary-encoded REAL truncated.");
let exponentLength = this.value[1];
if (this.value.length < (3 + exponentLength))
throw new ASN1TruncationError("Binary-encoded REAL truncated.");
exponent = asn1_ASN1Element.decodeSignedBigEndianInteger(this.value.subarray(2, (2 + exponentLength)));
mantissa = asn1_ASN1Element.decodeUnsignedBigEndianInteger(this.value.subarray((2 + exponentLength)));
break;
}
default:
throw new ASN1Error("Impossible binary REAL exponent encoding encountered.");
}
return (sign * mantissa * Math.pow(2, scale) * Math.pow(base, exponent));
}
default:
throw new ASN1Error("Impossible REAL format encountered.");
}
}
set utf8String(value) {
if (typeof TextEncoder !== "undefined") {
this.value = (new TextEncoder()).encode(value);
}
else if (typeof Buffer !== "undefined") {
this.value = Buffer.from(value, "utf-8");
}
}
get utf8String() {
const valueBytes = this.deconstruct("UTF8String");
let ret = "";
if (typeof TextEncoder !== "undefined") {
ret = (new TextDecoder("utf-8")).decode(valueBytes.buffer);
}
else if (typeof Buffer !== "undefined") {
ret = (new Buffer(this.value)).toString("utf-8");
}
return ret;
}
set sequence(value) {
let encodedElements = [];
value.forEach(element => {
encodedElements.push(element.toBytes());
});
let totalLength = 0;
encodedElements.forEach(element => {
totalLength += element.length;
});
const newValue = new Uint8Array(totalLength);
let currentIndex = 0;
encodedElements.forEach(element => {
newValue.set(element, currentIndex);
currentIndex += element.length;
});
this.value = newValue;
this.construction = 1;
}
get sequence() {
if (this.construction !== 1)
throw new ASN1ConstructionError("SET or SEQUENCE cannot be primitively constructed.");
let encodedElements = [];
if (this.value.length === 0)
return [];
let i = 0;
while (i < this.value.length) {
const next = new ber_BERElement();
i += next.fromBytes(this.value.slice(i));
encodedElements.push(next);
}
return encodedElements;
}
set set(value) {
this.sequence = value;
}
get set() {
return this.sequence;
}
set numericString(value) {
for (let i = 0; i < value.length; i++) {
const characterCode = value.charCodeAt(i);
if (!((characterCode >= 0x30 && characterCode <= 0x39) || characterCode === 0x20)) {
throw new ASN1CharactersError("NumericString can only contain characters 0 - 9 and space.");
}
}
if (typeof TextEncoder !== "undefined") {
this.value = (new TextEncoder()).encode(value);
}
else if (typeof Buffer !== "undefined") {
this.value = Buffer.from(value, "utf-8");
}
}
get numericString() {
const valueBytes = this.deconstruct("NumericString");
let ret = "";
if (typeof TextEncoder !== "undefined") {
ret = (new TextDecoder("utf-8")).decode(valueBytes.buffer);
}
else if (typeof Buffer !== "undefined") {
ret = (new Buffer(this.value)).toString("utf-8");
}
for (let i = 0; i < ret.length; i++) {
const characterCode = ret.charCodeAt(i);
if (!((characterCode >= 0x30 && characterCode <= 0x39) || characterCode === 0x20)) {
throw new ASN1CharactersError("NumericString can only contain characters 0 - 9 and space.");
}
}
return ret;
}
set printableString(value) {
for (let i = 0; i < value.length; i++) {
if (printableStringCharacters.indexOf(value.charAt(i)) === -1) {
throw new ASN1CharactersError(`PrintableString can only contain these characters: ${printableStringCharacters}`);
}
}
if (typeof TextEncoder !== "undefined") {
this.value = (new TextEncoder()).encode(value);
}
else if (typeof Buffer !== "undefined") {
this.value = Buffer.from(value, "utf-8");
}
}
get printableString() {
const valueBytes = this.deconstruct("PrintableString");
let ret = "";
if (typeof TextEncoder !== "undefined") {
ret = (new TextDecoder("utf-8")).decode(valueBytes.buffer);
}
else if (typeof Buffer !== "undefined") {
ret = (new Buffer(this.value)).toString("utf-8");
}
for (let i = 0; i < ret.length; i++) {
if (printableStringCharacters.indexOf(ret.charAt(i)) === -1) {
throw new ASN1CharactersError(`PrintableString can only contain these characters: ${printableStringCharacters}`);
}
}
return ret;
}
set teletexString(value) {
this.value = value.subarray(0);
}
get teletexString() {
return this.deconstruct("TeletexString");
}
set videotexString(value) {
this.value = value.subarray(0);
}
get videotexString() {
return this.deconstruct("VideotexString");
}
set ia5String(value) {
if (typeof TextEncoder !== "undefined") {
this.value = (new TextEncoder()).encode(value);
}
else if (typeof Buffer !== "undefined") {
this.value = Buffer.from(value, "utf-8");
}
}
get ia5String() {
const valueBytes = this.deconstruct("IA5String");
let ret = "";
if (typeof TextEncoder !== "undefined") {
ret = (new TextDecoder("utf-8")).decode(valueBytes.buffer);
}
else if (typeof Buffer !== "undefined") {
ret = (new Buffer(this.value)).toString("utf-8");
}
return ret;
}
set utcTime(value) {
let year = value.getUTCFullYear().toString();
year = (year.substring(year.length - 2, year.length));
const month = (value.getUTCMonth() < 9 ? `0${value.getUTCMonth() + 1}` : `${value.getUTCMonth() + 1}`);
const day = (value.getUTCDate() < 10 ? `0${value.getUTCDate()}` : `${value.getUTCDate()}`);
const hour = (value.getUTCHours() < 10 ? `0${value.getUTCHours()}` : `${value.getUTCHours()}`);
const minute = (value.getUTCMinutes() < 10 ? `0${value.getUTCMinutes()}` : `${value.getUTCMinutes()}`);
const second = (value.getUTCSeconds() < 10 ? `0${value.getUTCSeconds()}` : `${value.getUTCSeconds()}`);
const utcString = `${year}${month}${day}${hour}${minute}${second}Z`;
if (typeof TextEncoder !== "undefined") {
this.value = (new TextEncoder()).encode(utcString);
}
else if (typeof Buffer !== "undefined") {
this.value = Buffer.from(utcString, "utf-8");
}
}
get utcTime() {
const valueBytes = this.deconstruct("UTCTime");
let dateString = "";
if (typeof TextEncoder !== "undefined") {
dateString = (new TextDecoder("utf-8")).decode(valueBytes.buffer);
}
else if (typeof Buffer !== "undefined") {
dateString = (new Buffer(this.value)).toString("utf-8");
}
const match = utcTimeRegex.exec(dateString);
if (match === null)
throw new ASN1Error("Malformed UTCTime string.");
if (match.groups === undefined)
throw new ASN1Error("Malformed UTCTime string.");
const ret = new Date();
let year = Number(match.groups.year);
year = (year < 70 ? (2000 + year) : (1900 + year));
const month = (Number(match.groups.month) - 1);
const date = Number(match.groups.date);
const hours = Number(match.groups.hour);
const minutes = Number(match.groups.minute);
const seconds = Number(match.groups.second);
ber_BERElement.validateDateTime("UTCTime", year, month, date, hours, minutes, seconds);
ret.setUTCFullYear(year);
ret.setUTCMonth(month);
ret.setUTCDate(date);
ret.setUTCHours(hours);
ret.setUTCMinutes(minutes);
ret.setUTCSeconds(seconds);
return ret;
}
set generalizedTime(value) {
const year = value.getUTCFullYear().toString();
const month = (value.getUTCMonth() < 9 ? `0${value.getUTCMonth() + 1}` : `${value.getUTCMonth() + 1}`);
const day = (value.getUTCDate() < 10 ? `0${value.getUTCDate()}` : `${value.getUTCDate()}`);
const hour = (value.getUTCHours() < 10 ? `0${value.getUTCHours()}` : `${value.getUTCHours()}`);
const minute = (value.getUTCMinutes() < 10 ? `0${value.getUTCMinutes()}` : `${value.getUTCMinutes()}`);
const second = (value.getUTCSeconds() < 10 ? `0${value.getUTCSeconds()}` : `${value.getUTCSeconds()}`);
const timeString = `${year}${month}${day}${hour}${minute}${second}Z`;
if (typeof TextEncoder !== "undefined") {
this.value = (new TextEncoder()).encode(timeString);
}
else if (typeof Buffer !== "undefined") {
this.value = Buffer.from(timeString, "utf-8");
}
}
get generalizedTime() {
const valueBytes = this.deconstruct("GeneralizedTime");
let dateString = "";
if (typeof TextEncoder !== "undefined") {
dateString = (new TextDecoder("utf-8")).decode(valueBytes.buffer);
}
else if (typeof Buffer !== "undefined") {
dateString = (new Buffer(this.value)).toString("utf-8");
}
const match = generalizedTimeRegex.exec(dateString);
if (match === null)
throw new ASN1Error("Malformed GeneralizedTime string.");
if (match.groups === undefined)
throw new ASN1Error("Malformed GeneralizedTime string.");
const ret = new Date();
const year = Number(match.groups.year);
const month = (Number(match.groups.month) - 1);
const date = Number(match.groups.date);
const hours = Number(match.groups.hour);
const minutes = Number(match.groups.minute);
const seconds = Number(match.groups.second);
ber_BERElement.validateDateTime("GeneralizedTime", year, month, date, hours, minutes, seconds);
ret.setUTCFullYear(year);
ret.setUTCMonth(month);
ret.setUTCDate(date);
ret.setUTCHours(hours);
ret.setUTCMinutes(minutes);
ret.setUTCSeconds(seconds);
return ret;
}
set graphicString(value) {
for (let i = 0; i < value.length; i++) {
const characterCode = value.charCodeAt(i);
if (characterCode < 0x20 || characterCode > 0x7E)
throw new ASN1CharactersError("GraphicString, VisibleString, or ObjectDescriptor " +
"can only contain characters between 0x20 and 0x7E.");
}
if (typeof TextEncoder !== "undefined") {
this.value = (new TextEncoder()).encode(value);
}
else if (typeof Buffer !== "undefined") {
this.value = Buffer.from(value, "utf-8");
}
}
get graphicString() {
const valueBytes = this.deconstruct("GraphicString, VisibleString, or ObjectDescriptor");
let ret = "";
if (typeof TextEncoder !== "undefined") {
ret = (new TextDecoder("utf-8")).decode(valueBytes.buffer);
}
else if (typeof Buffer !== "undefined") {
ret = (new Buffer(this.value)).toString("utf-8");
}
for (let i = 0; i < ret.length; i++) {
const characterCode = ret.charCodeAt(i);
if (characterCode < 0x20 || characterCode > 0x7E) {
throw new ASN1CharactersError("GraphicString, VisibleString, or ObjectDescriptor " +
"can only contain characters between 0x20 and 0x7E.");
}
}
return ret;
}
set visibleString(value) {
this.graphicString = value;
}
get visibleString() {
return this.graphicString;
}
set generalString(value) {
for (let i = 0; i < value.length; i++) {
if (value.charCodeAt(i) > 0x7F)
throw new ASN1CharactersError("GeneralString can only contain ASCII characters.");
}
if (typeof TextEncoder !== "undefined") {
this.value = (new TextEncoder()).encode(value);
}
else if (typeof Buffer !== "undefined") {
this.value = Buffer.from(value, "ascii");
}
}
get generalString() {
const valueBytes = this.deconstruct("GeneralString");
let ret = "";
if (typeof TextEncoder !== "undefined") {
ret = (new TextDecoder("windows-1252")).decode(valueBytes.buffer);
}
else if (typeof Buffer !== "undefined") {
ret = (new Buffer(this.value)).toString("ascii");
}
for (let i = 0; i < ret.length; i++) {
if (ret.charCodeAt(i) > 0x7F)
throw new ASN1CharactersError("GeneralString can only contain ASCII characters.");
}
return ret;
}
set universalString(value) {
const buf = new Uint8Array(value.length << 2);
for (let i = 0; i < value.length; i++) {
buf[(i << 2)] = value.charCodeAt(i) >>> 24;
buf[(i << 2) + 1] = value.charCodeAt(i) >>> 16;
buf[(i << 2) + 2] = value.charCodeAt(i) >>> 8;
buf[(i << 2) + 3] = value.charCodeAt(i);
}
this.value = buf;
}
get universalString() {
const valueBytes = this.deconstruct("UniversalString");
if (valueBytes.length % 4)
throw new ASN1Error("UniversalString encoded on non-mulitple of four bytes.");
let ret = "";
for (let i = 0; i < valueBytes.length; i += 4) {
ret += String.fromCharCode((valueBytes[i + 0] << 24) +
(valueBytes[i + 1] << 16) +
(valueBytes[i + 2] << 8) +
(valueBytes[i + 3] << 0));
}
return ret;
}
set bmpString(value) {
const buf = new Uint8Array(value.length << 1);
for (let i = 0, strLen = value.length; i < strLen; i++) {
buf[(i << 1)] = value.charCodeAt(i) >>> 8;
buf[(i << 1) + 1] = value.charCodeAt(i);
}
this.value = buf;
}
get bmpString() {
const valueBytes = this.deconstruct("BMPString");
if (valueBytes.length % 2)
throw new ASN1Error("BMPString encoded on non-mulitple of two bytes.");
let ret = "";
if (typeof TextEncoder !== "undefined") {
ret = (new TextDecoder("utf-16be")).decode(valueBytes.buffer);
}
else if (typeof Buffer !== "undefined") {
const swappedEndianness = new Uint8Array(valueBytes.length);
for (let i = 0; i < valueBytes.length; i += 2) {
swappedEndianness[i] = valueBytes[i + 1];
swappedEndianness[i + 1] = valueBytes[i];
}
ret = (new Buffer(swappedEndianness)).toString("utf-16le");
}
return ret;
}
fromBytes(bytes) {
if (bytes.length < 2)
throw new ASN1TruncationError("Tried to decode a BER element that is less than two bytes.");
if ((this.recursionCount + 1) > ber_BERElement.nestingRecursionLimit)
throw new ASN1RecursionError();
let cursor = 0;
switch (bytes[cursor] & 0b11000000) {
case (0b00000000):
this.tagClass = 0;
break;
case (0b01000000):
this.tagClass = 1;
break;
case (0b10000000):
this.tagClass = 3;
break;
case (0b11000000):
this.tagClass = 4;
break;
default: this.tagClass = 0;
}
this.construction = ((bytes[cursor] & 0b00100000) ?
1 : 0);
this.tagNumber = (bytes[cursor] & 0b00011111);
cursor++;
if (this.tagNumber >= 31) {
if (bytes[cursor] === 0b10000000)
throw new ASN1PaddingError("Leading padding byte on long tag number encoding.");
this.tagNumber = 0;
const limit = (((bytes.length - 1) >= 4) ? 4 : (bytes.length - 1));
while (cursor < limit) {
if (!(bytes[cursor++] & 0b10000000))
break;
}
if (bytes[cursor - 1] & 0b10000000) {
if (limit === bytes.length - 1) {
throw new ASN1TruncationError("ASN.1 tag number appears to have been truncated.");
}
else
throw new ASN1OverflowError("ASN.1 tag number too large.");
}
for (let i = 1; i < cursor; i++) {
this.tagNumber <<= 7;
this.tagNumber |= (bytes[i] & 0x7F);
}
if (this.tagNumber <= 31)
throw new ASN1Error("ASN.1 tag number could have been encoded in short form.");
}
if ((bytes[cursor] & 0b10000000) === 0b10000000) {
const numberOfLengthOctets = (bytes[cursor] & 0x7F);
if (numberOfLengthOctets) {
if (numberOfLengthOctets === 0b01111111)
throw new ASN1UndefinedError("Length byte with undefined meaning encountered.");
if (numberOfLengthOctets > 4)
throw new ASN1OverflowError("Element length too long to decode to an integer.");
if (cursor + numberOfLengthOctets >= bytes.length)
throw new ASN1TruncationError("Element length bytes appear to have been truncated.");
cursor++;
const lengthNumberOctets = new Uint8Array(4);
for (let i = numberOfLengthOctets; i > 0; i--) {
lengthNumberOctets[(4 - i)] = bytes[(cursor + numberOfLengthOctets - i)];
}
let length = 0;
lengthNumberOctets.forEach(octet => {
length <<= 8;
length += octet;
});
if ((cursor + length) < cursor)
throw new ASN1OverflowError("ASN.1 element too large.");
cursor += (numberOfLengthOctets);
if ((cursor + length) > bytes.length)
throw new ASN1TruncationError("ASN.1 element