webcrypto-liner
Version:
A WebCrypto polyfill that "smooths out" the rough-edges in existing User Agent implementations.
1,496 lines (1,495 loc) • 338 kB
JavaScript
var liner = function (exports, asmCrypto, elliptic) {
'use strict';
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () {
return e[k];
}
});
}
});
}
n.default = e;
return Object.freeze(n);
}
function _mergeNamespaces(n, m) {
m.forEach(function (e) {
e && typeof e !== 'string' && !Array.isArray(e) && Object.keys(e).forEach(function (k) {
if (k !== 'default' && !(k in n)) {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () {
return e[k];
}
});
}
});
});
return Object.freeze(n);
}
var asmCrypto__namespace = _interopNamespaceDefault(asmCrypto);
var elliptic__namespace = _interopNamespaceDefault(elliptic);
let window$2 = {};
if (typeof self !== "undefined") {
window$2 = self;
}
exports.nativeCrypto = window$2["msCrypto"] || window$2.crypto || {};
exports.nativeSubtle = null;
try {
exports.nativeSubtle = (exports.nativeCrypto === null || exports.nativeCrypto === void 0 ? void 0 : exports.nativeCrypto.subtle) || (exports.nativeCrypto === null || exports.nativeCrypto === void 0 ? void 0 : exports.nativeCrypto["webkitSubtle"]) || null;
} catch (err) {
console.warn("Cannot get subtle from crypto", err);
}
function setCrypto(crypto) {
exports.nativeCrypto = crypto;
exports.nativeSubtle = crypto.subtle;
}
const ARRAY_BUFFER_NAME = "[object ArrayBuffer]";
class BufferSourceConverter {
static isArrayBuffer(data) {
return Object.prototype.toString.call(data) === ARRAY_BUFFER_NAME;
}
static toArrayBuffer(data) {
if (this.isArrayBuffer(data)) {
return data;
}
if (data.byteLength === data.buffer.byteLength) {
return data.buffer;
}
if (data.byteOffset === 0 && data.byteLength === data.buffer.byteLength) {
return data.buffer;
}
return this.toUint8Array(data.buffer).slice(data.byteOffset, data.byteOffset + data.byteLength).buffer;
}
static toUint8Array(data) {
return this.toView(data, Uint8Array);
}
static toView(data, type) {
if (data.constructor === type) {
return data;
}
if (this.isArrayBuffer(data)) {
return new type(data);
}
if (this.isArrayBufferView(data)) {
return new type(data.buffer, data.byteOffset, data.byteLength);
}
throw new TypeError("The provided value is not of type '(ArrayBuffer or ArrayBufferView)'");
}
static isBufferSource(data) {
return this.isArrayBufferView(data) || this.isArrayBuffer(data);
}
static isArrayBufferView(data) {
return ArrayBuffer.isView(data) || data && this.isArrayBuffer(data.buffer);
}
static isEqual(a, b) {
const aView = BufferSourceConverter.toUint8Array(a);
const bView = BufferSourceConverter.toUint8Array(b);
if (aView.length !== bView.byteLength) {
return false;
}
for (let i = 0; i < aView.length; i++) {
if (aView[i] !== bView[i]) {
return false;
}
}
return true;
}
static concat(...args) {
let buffers;
if (Array.isArray(args[0]) && !(args[1] instanceof Function)) {
buffers = args[0];
} else if (Array.isArray(args[0]) && args[1] instanceof Function) {
buffers = args[0];
} else {
if (args[args.length - 1] instanceof Function) {
buffers = args.slice(0, args.length - 1);
} else {
buffers = args;
}
}
let size = 0;
for (const buffer of buffers) {
size += buffer.byteLength;
}
const res = new Uint8Array(size);
let offset = 0;
for (const buffer of buffers) {
const view = this.toUint8Array(buffer);
res.set(view, offset);
offset += view.length;
}
if (args[args.length - 1] instanceof Function) {
return this.toView(res, args[args.length - 1]);
}
return res.buffer;
}
}
const STRING_TYPE = "string";
const HEX_REGEX = /^[0-9a-f]+$/i;
const BASE64_REGEX = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
const BASE64URL_REGEX = /^[a-zA-Z0-9-_]+$/;
class Utf8Converter {
static fromString(text) {
const s = unescape(encodeURIComponent(text));
const uintArray = new Uint8Array(s.length);
for (let i = 0; i < s.length; i++) {
uintArray[i] = s.charCodeAt(i);
}
return uintArray.buffer;
}
static toString(buffer) {
const buf = BufferSourceConverter.toUint8Array(buffer);
let encodedString = "";
for (let i = 0; i < buf.length; i++) {
encodedString += String.fromCharCode(buf[i]);
}
const decodedString = decodeURIComponent(escape(encodedString));
return decodedString;
}
}
class Utf16Converter {
static toString(buffer, littleEndian = false) {
const arrayBuffer = BufferSourceConverter.toArrayBuffer(buffer);
const dataView = new DataView(arrayBuffer);
let res = "";
for (let i = 0; i < arrayBuffer.byteLength; i += 2) {
const code = dataView.getUint16(i, littleEndian);
res += String.fromCharCode(code);
}
return res;
}
static fromString(text, littleEndian = false) {
const res = new ArrayBuffer(text.length * 2);
const dataView = new DataView(res);
for (let i = 0; i < text.length; i++) {
dataView.setUint16(i * 2, text.charCodeAt(i), littleEndian);
}
return res;
}
}
class Convert {
static isHex(data) {
return typeof data === STRING_TYPE && HEX_REGEX.test(data);
}
static isBase64(data) {
return typeof data === STRING_TYPE && BASE64_REGEX.test(data);
}
static isBase64Url(data) {
return typeof data === STRING_TYPE && BASE64URL_REGEX.test(data);
}
static ToString(buffer, enc = "utf8") {
const buf = BufferSourceConverter.toUint8Array(buffer);
switch (enc.toLowerCase()) {
case "utf8":
return this.ToUtf8String(buf);
case "binary":
return this.ToBinary(buf);
case "hex":
return this.ToHex(buf);
case "base64":
return this.ToBase64(buf);
case "base64url":
return this.ToBase64Url(buf);
case "utf16le":
return Utf16Converter.toString(buf, true);
case "utf16":
case "utf16be":
return Utf16Converter.toString(buf);
default:
throw new Error(`Unknown type of encoding '${enc}'`);
}
}
static FromString(str, enc = "utf8") {
if (!str) {
return new ArrayBuffer(0);
}
switch (enc.toLowerCase()) {
case "utf8":
return this.FromUtf8String(str);
case "binary":
return this.FromBinary(str);
case "hex":
return this.FromHex(str);
case "base64":
return this.FromBase64(str);
case "base64url":
return this.FromBase64Url(str);
case "utf16le":
return Utf16Converter.fromString(str, true);
case "utf16":
case "utf16be":
return Utf16Converter.fromString(str);
default:
throw new Error(`Unknown type of encoding '${enc}'`);
}
}
static ToBase64(buffer) {
const buf = BufferSourceConverter.toUint8Array(buffer);
if (typeof btoa !== "undefined") {
const binary = this.ToString(buf, "binary");
return btoa(binary);
} else {
return Buffer.from(buf).toString("base64");
}
}
static FromBase64(base64) {
const formatted = this.formatString(base64);
if (!formatted) {
return new ArrayBuffer(0);
}
if (!Convert.isBase64(formatted)) {
throw new TypeError("Argument 'base64Text' is not Base64 encoded");
}
if (typeof atob !== "undefined") {
return this.FromBinary(atob(formatted));
} else {
return new Uint8Array(Buffer.from(formatted, "base64")).buffer;
}
}
static FromBase64Url(base64url) {
const formatted = this.formatString(base64url);
if (!formatted) {
return new ArrayBuffer(0);
}
if (!Convert.isBase64Url(formatted)) {
throw new TypeError("Argument 'base64url' is not Base64Url encoded");
}
return this.FromBase64(this.Base64Padding(formatted.replace(/\-/g, "+").replace(/\_/g, "/")));
}
static ToBase64Url(data) {
return this.ToBase64(data).replace(/\+/g, "-").replace(/\//g, "_").replace(/\=/g, "");
}
static FromUtf8String(text, encoding = Convert.DEFAULT_UTF8_ENCODING) {
switch (encoding) {
case "ascii":
return this.FromBinary(text);
case "utf8":
return Utf8Converter.fromString(text);
case "utf16":
case "utf16be":
return Utf16Converter.fromString(text);
case "utf16le":
case "usc2":
return Utf16Converter.fromString(text, true);
default:
throw new Error(`Unknown type of encoding '${encoding}'`);
}
}
static ToUtf8String(buffer, encoding = Convert.DEFAULT_UTF8_ENCODING) {
switch (encoding) {
case "ascii":
return this.ToBinary(buffer);
case "utf8":
return Utf8Converter.toString(buffer);
case "utf16":
case "utf16be":
return Utf16Converter.toString(buffer);
case "utf16le":
case "usc2":
return Utf16Converter.toString(buffer, true);
default:
throw new Error(`Unknown type of encoding '${encoding}'`);
}
}
static FromBinary(text) {
const stringLength = text.length;
const resultView = new Uint8Array(stringLength);
for (let i = 0; i < stringLength; i++) {
resultView[i] = text.charCodeAt(i);
}
return resultView.buffer;
}
static ToBinary(buffer) {
const buf = BufferSourceConverter.toUint8Array(buffer);
let res = "";
for (let i = 0; i < buf.length; i++) {
res += String.fromCharCode(buf[i]);
}
return res;
}
static ToHex(buffer) {
const buf = BufferSourceConverter.toUint8Array(buffer);
let result = "";
const len = buf.length;
for (let i = 0; i < len; i++) {
const byte = buf[i];
if (byte < 16) {
result += "0";
}
result += byte.toString(16);
}
return result;
}
static FromHex(hexString) {
let formatted = this.formatString(hexString);
if (!formatted) {
return new ArrayBuffer(0);
}
if (!Convert.isHex(formatted)) {
throw new TypeError("Argument 'hexString' is not HEX encoded");
}
if (formatted.length % 2) {
formatted = `0${formatted}`;
}
const res = new Uint8Array(formatted.length / 2);
for (let i = 0; i < formatted.length; i = i + 2) {
const c = formatted.slice(i, i + 2);
res[i / 2] = parseInt(c, 16);
}
return res.buffer;
}
static ToUtf16String(buffer, littleEndian = false) {
return Utf16Converter.toString(buffer, littleEndian);
}
static FromUtf16String(text, littleEndian = false) {
return Utf16Converter.fromString(text, littleEndian);
}
static Base64Padding(base64) {
const padCount = 4 - base64.length % 4;
if (padCount < 4) {
for (let i = 0; i < padCount; i++) {
base64 += "=";
}
}
return base64;
}
static formatString(data) {
return (data === null || data === void 0 ? void 0 : data.replace(/[\n\r\t ]/g, "")) || "";
}
}
Convert.DEFAULT_UTF8_ENCODING = "utf8";
function combine(...buf) {
const totalByteLength = buf.map(item => item.byteLength).reduce((prev, cur) => prev + cur);
const res = new Uint8Array(totalByteLength);
let currentPos = 0;
buf.map(item => new Uint8Array(item)).forEach(arr => {
for (const item2 of arr) {
res[currentPos++] = item2;
}
});
return res.buffer;
}
function utilFromBase(inputBuffer, inputBase) {
let result = 0;
if (inputBuffer.length === 1) {
return inputBuffer[0];
}
for (let i = inputBuffer.length - 1; i >= 0; i--) {
result += inputBuffer[inputBuffer.length - 1 - i] * Math.pow(2, inputBase * i);
}
return result;
}
function utilToBase(value, base, reserved = -1) {
const internalReserved = reserved;
let internalValue = value;
let result = 0;
let biggest = Math.pow(2, base);
for (let i = 1; i < 8; i++) {
if (value < biggest) {
let retBuf;
if (internalReserved < 0) {
retBuf = new ArrayBuffer(i);
result = i;
} else {
if (internalReserved < i) {
return new ArrayBuffer(0);
}
retBuf = new ArrayBuffer(internalReserved);
result = internalReserved;
}
const retView = new Uint8Array(retBuf);
for (let j = i - 1; j >= 0; j--) {
const basis = Math.pow(2, j * base);
retView[result - j - 1] = Math.floor(internalValue / basis);
internalValue -= retView[result - j - 1] * basis;
}
return retBuf;
}
biggest *= Math.pow(2, base);
}
return new ArrayBuffer(0);
}
function utilConcatView(...views) {
let outputLength = 0;
let prevLength = 0;
for (const view of views) {
outputLength += view.length;
}
const retBuf = new ArrayBuffer(outputLength);
const retView = new Uint8Array(retBuf);
for (const view of views) {
retView.set(view, prevLength);
prevLength += view.length;
}
return retView;
}
function utilDecodeTC() {
const buf = new Uint8Array(this.valueHex);
if (this.valueHex.byteLength >= 2) {
const condition1 = buf[0] === 0xFF && buf[1] & 0x80;
const condition2 = buf[0] === 0x00 && (buf[1] & 0x80) === 0x00;
if (condition1 || condition2) {
this.warnings.push("Needlessly long format");
}
}
const bigIntBuffer = new ArrayBuffer(this.valueHex.byteLength);
const bigIntView = new Uint8Array(bigIntBuffer);
for (let i = 0; i < this.valueHex.byteLength; i++) {
bigIntView[i] = 0;
}
bigIntView[0] = buf[0] & 0x80;
const bigInt = utilFromBase(bigIntView, 8);
const smallIntBuffer = new ArrayBuffer(this.valueHex.byteLength);
const smallIntView = new Uint8Array(smallIntBuffer);
for (let j = 0; j < this.valueHex.byteLength; j++) {
smallIntView[j] = buf[j];
}
smallIntView[0] &= 0x7F;
const smallInt = utilFromBase(smallIntView, 8);
return smallInt - bigInt;
}
function utilEncodeTC(value) {
const modValue = value < 0 ? value * -1 : value;
let bigInt = 128;
for (let i = 1; i < 8; i++) {
if (modValue <= bigInt) {
if (value < 0) {
const smallInt = bigInt - modValue;
const retBuf = utilToBase(smallInt, 8, i);
const retView = new Uint8Array(retBuf);
retView[0] |= 0x80;
return retBuf;
}
let retBuf = utilToBase(modValue, 8, i);
let retView = new Uint8Array(retBuf);
if (retView[0] & 0x80) {
const tempBuf = retBuf.slice(0);
const tempView = new Uint8Array(tempBuf);
retBuf = new ArrayBuffer(retBuf.byteLength + 1);
retView = new Uint8Array(retBuf);
for (let k = 0; k < tempBuf.byteLength; k++) {
retView[k + 1] = tempView[k];
}
retView[0] = 0x00;
}
return retBuf;
}
bigInt *= Math.pow(2, 8);
}
return new ArrayBuffer(0);
}
function isEqualBuffer(inputBuffer1, inputBuffer2) {
if (inputBuffer1.byteLength !== inputBuffer2.byteLength) {
return false;
}
const view1 = new Uint8Array(inputBuffer1);
const view2 = new Uint8Array(inputBuffer2);
for (let i = 0; i < view1.length; i++) {
if (view1[i] !== view2[i]) {
return false;
}
}
return true;
}
function padNumber(inputNumber, fullLength) {
const str = inputNumber.toString(10);
if (fullLength < str.length) {
return "";
}
const dif = fullLength - str.length;
const padding = new Array(dif);
for (let i = 0; i < dif; i++) {
padding[i] = "0";
}
const paddingString = padding.join("");
return paddingString.concat(str);
}
function assertBigInt() {
if (typeof BigInt === "undefined") {
throw new Error("BigInt is not defined. Your environment doesn't implement BigInt.");
}
}
function concat$1(buffers) {
let outputLength = 0;
let prevLength = 0;
for (let i = 0; i < buffers.length; i++) {
const buffer = buffers[i];
outputLength += buffer.byteLength;
}
const retView = new Uint8Array(outputLength);
for (let i = 0; i < buffers.length; i++) {
const buffer = buffers[i];
retView.set(new Uint8Array(buffer), prevLength);
prevLength += buffer.byteLength;
}
return retView.buffer;
}
function checkBufferParams(baseBlock, inputBuffer, inputOffset, inputLength) {
if (!(inputBuffer instanceof Uint8Array)) {
baseBlock.error = "Wrong parameter: inputBuffer must be 'Uint8Array'";
return false;
}
if (!inputBuffer.byteLength) {
baseBlock.error = "Wrong parameter: inputBuffer has zero length";
return false;
}
if (inputOffset < 0) {
baseBlock.error = "Wrong parameter: inputOffset less than zero";
return false;
}
if (inputLength < 0) {
baseBlock.error = "Wrong parameter: inputLength less than zero";
return false;
}
if (inputBuffer.byteLength - inputOffset - inputLength < 0) {
baseBlock.error = "End of input reached before message was fully decoded (inconsistent offset and length values)";
return false;
}
return true;
}
class ViewWriter {
constructor() {
this.items = [];
}
write(buf) {
this.items.push(buf);
}
final() {
return concat$1(this.items);
}
}
const powers2 = [new Uint8Array([1])];
const digitsString = "0123456789";
const NAME = "name";
const VALUE_HEX_VIEW = "valueHexView";
const IS_HEX_ONLY = "isHexOnly";
const ID_BLOCK = "idBlock";
const TAG_CLASS = "tagClass";
const TAG_NUMBER = "tagNumber";
const IS_CONSTRUCTED = "isConstructed";
const FROM_BER = "fromBER";
const TO_BER = "toBER";
const LOCAL = "local";
const EMPTY_STRING = "";
const EMPTY_BUFFER = new ArrayBuffer(0);
const EMPTY_VIEW = new Uint8Array(0);
const END_OF_CONTENT_NAME = "EndOfContent";
const OCTET_STRING_NAME = "OCTET STRING";
const BIT_STRING_NAME = "BIT STRING";
function HexBlock(BaseClass) {
var _a;
return _a = class Some extends BaseClass {
constructor(...args) {
var _a;
super(...args);
const params = args[0] || {};
this.isHexOnly = (_a = params.isHexOnly) !== null && _a !== void 0 ? _a : false;
this.valueHexView = params.valueHex ? BufferSourceConverter.toUint8Array(params.valueHex) : EMPTY_VIEW;
}
get valueHex() {
return this.valueHexView.slice().buffer;
}
set valueHex(value) {
this.valueHexView = new Uint8Array(value);
}
fromBER(inputBuffer, inputOffset, inputLength) {
const view = inputBuffer instanceof ArrayBuffer ? new Uint8Array(inputBuffer) : inputBuffer;
if (!checkBufferParams(this, view, inputOffset, inputLength)) {
return -1;
}
const endLength = inputOffset + inputLength;
this.valueHexView = view.subarray(inputOffset, endLength);
if (!this.valueHexView.length) {
this.warnings.push("Zero buffer length");
return inputOffset;
}
this.blockLength = inputLength;
return endLength;
}
toBER(sizeOnly = false) {
if (!this.isHexOnly) {
this.error = "Flag 'isHexOnly' is not set, abort";
return EMPTY_BUFFER;
}
if (sizeOnly) {
return new ArrayBuffer(this.valueHexView.byteLength);
}
return this.valueHexView.byteLength === this.valueHexView.buffer.byteLength ? this.valueHexView.buffer : this.valueHexView.slice().buffer;
}
toJSON() {
return {
...super.toJSON(),
isHexOnly: this.isHexOnly,
valueHex: Convert.ToHex(this.valueHexView)
};
}
}, _a.NAME = "hexBlock", _a;
}
class LocalBaseBlock {
constructor({
blockLength = 0,
error = EMPTY_STRING,
warnings = [],
valueBeforeDecode = EMPTY_VIEW
} = {}) {
this.blockLength = blockLength;
this.error = error;
this.warnings = warnings;
this.valueBeforeDecodeView = BufferSourceConverter.toUint8Array(valueBeforeDecode);
}
static blockName() {
return this.NAME;
}
get valueBeforeDecode() {
return this.valueBeforeDecodeView.slice().buffer;
}
set valueBeforeDecode(value) {
this.valueBeforeDecodeView = new Uint8Array(value);
}
toJSON() {
return {
blockName: this.constructor.NAME,
blockLength: this.blockLength,
error: this.error,
warnings: this.warnings,
valueBeforeDecode: Convert.ToHex(this.valueBeforeDecodeView)
};
}
}
LocalBaseBlock.NAME = "baseBlock";
class ValueBlock extends LocalBaseBlock {
fromBER(inputBuffer, inputOffset, inputLength) {
throw TypeError("User need to make a specific function in a class which extends 'ValueBlock'");
}
toBER(sizeOnly, writer) {
throw TypeError("User need to make a specific function in a class which extends 'ValueBlock'");
}
}
ValueBlock.NAME = "valueBlock";
class LocalIdentificationBlock extends HexBlock(LocalBaseBlock) {
constructor({
idBlock = {}
} = {}) {
var _a, _b, _c, _d;
super();
if (idBlock) {
this.isHexOnly = (_a = idBlock.isHexOnly) !== null && _a !== void 0 ? _a : false;
this.valueHexView = idBlock.valueHex ? BufferSourceConverter.toUint8Array(idBlock.valueHex) : EMPTY_VIEW;
this.tagClass = (_b = idBlock.tagClass) !== null && _b !== void 0 ? _b : -1;
this.tagNumber = (_c = idBlock.tagNumber) !== null && _c !== void 0 ? _c : -1;
this.isConstructed = (_d = idBlock.isConstructed) !== null && _d !== void 0 ? _d : false;
} else {
this.tagClass = -1;
this.tagNumber = -1;
this.isConstructed = false;
}
}
toBER(sizeOnly = false) {
let firstOctet = 0;
switch (this.tagClass) {
case 1:
firstOctet |= 0x00;
break;
case 2:
firstOctet |= 0x40;
break;
case 3:
firstOctet |= 0x80;
break;
case 4:
firstOctet |= 0xC0;
break;
default:
this.error = "Unknown tag class";
return EMPTY_BUFFER;
}
if (this.isConstructed) firstOctet |= 0x20;
if (this.tagNumber < 31 && !this.isHexOnly) {
const retView = new Uint8Array(1);
if (!sizeOnly) {
let number = this.tagNumber;
number &= 0x1F;
firstOctet |= number;
retView[0] = firstOctet;
}
return retView.buffer;
}
if (!this.isHexOnly) {
const encodedBuf = utilToBase(this.tagNumber, 7);
const encodedView = new Uint8Array(encodedBuf);
const size = encodedBuf.byteLength;
const retView = new Uint8Array(size + 1);
retView[0] = firstOctet | 0x1F;
if (!sizeOnly) {
for (let i = 0; i < size - 1; i++) retView[i + 1] = encodedView[i] | 0x80;
retView[size] = encodedView[size - 1];
}
return retView.buffer;
}
const retView = new Uint8Array(this.valueHexView.byteLength + 1);
retView[0] = firstOctet | 0x1F;
if (!sizeOnly) {
const curView = this.valueHexView;
for (let i = 0; i < curView.length - 1; i++) retView[i + 1] = curView[i] | 0x80;
retView[this.valueHexView.byteLength] = curView[curView.length - 1];
}
return retView.buffer;
}
fromBER(inputBuffer, inputOffset, inputLength) {
const inputView = BufferSourceConverter.toUint8Array(inputBuffer);
if (!checkBufferParams(this, inputView, inputOffset, inputLength)) {
return -1;
}
const intBuffer = inputView.subarray(inputOffset, inputOffset + inputLength);
if (intBuffer.length === 0) {
this.error = "Zero buffer length";
return -1;
}
const tagClassMask = intBuffer[0] & 0xC0;
switch (tagClassMask) {
case 0x00:
this.tagClass = 1;
break;
case 0x40:
this.tagClass = 2;
break;
case 0x80:
this.tagClass = 3;
break;
case 0xC0:
this.tagClass = 4;
break;
default:
this.error = "Unknown tag class";
return -1;
}
this.isConstructed = (intBuffer[0] & 0x20) === 0x20;
this.isHexOnly = false;
const tagNumberMask = intBuffer[0] & 0x1F;
if (tagNumberMask !== 0x1F) {
this.tagNumber = tagNumberMask;
this.blockLength = 1;
} else {
let count = 1;
let intTagNumberBuffer = this.valueHexView = new Uint8Array(255);
let tagNumberBufferMaxLength = 255;
while (intBuffer[count] & 0x80) {
intTagNumberBuffer[count - 1] = intBuffer[count] & 0x7F;
count++;
if (count >= intBuffer.length) {
this.error = "End of input reached before message was fully decoded";
return -1;
}
if (count === tagNumberBufferMaxLength) {
tagNumberBufferMaxLength += 255;
const tempBufferView = new Uint8Array(tagNumberBufferMaxLength);
for (let i = 0; i < intTagNumberBuffer.length; i++) tempBufferView[i] = intTagNumberBuffer[i];
intTagNumberBuffer = this.valueHexView = new Uint8Array(tagNumberBufferMaxLength);
}
}
this.blockLength = count + 1;
intTagNumberBuffer[count - 1] = intBuffer[count] & 0x7F;
const tempBufferView = new Uint8Array(count);
for (let i = 0; i < count; i++) tempBufferView[i] = intTagNumberBuffer[i];
intTagNumberBuffer = this.valueHexView = new Uint8Array(count);
intTagNumberBuffer.set(tempBufferView);
if (this.blockLength <= 9) this.tagNumber = utilFromBase(intTagNumberBuffer, 7);else {
this.isHexOnly = true;
this.warnings.push("Tag too long, represented as hex-coded");
}
}
if (this.tagClass === 1 && this.isConstructed) {
switch (this.tagNumber) {
case 1:
case 2:
case 5:
case 6:
case 9:
case 13:
case 14:
case 23:
case 24:
case 31:
case 32:
case 33:
case 34:
this.error = "Constructed encoding used for primitive type";
return -1;
}
}
return inputOffset + this.blockLength;
}
toJSON() {
return {
...super.toJSON(),
tagClass: this.tagClass,
tagNumber: this.tagNumber,
isConstructed: this.isConstructed
};
}
}
LocalIdentificationBlock.NAME = "identificationBlock";
class LocalLengthBlock extends LocalBaseBlock {
constructor({
lenBlock = {}
} = {}) {
var _a, _b, _c;
super();
this.isIndefiniteForm = (_a = lenBlock.isIndefiniteForm) !== null && _a !== void 0 ? _a : false;
this.longFormUsed = (_b = lenBlock.longFormUsed) !== null && _b !== void 0 ? _b : false;
this.length = (_c = lenBlock.length) !== null && _c !== void 0 ? _c : 0;
}
fromBER(inputBuffer, inputOffset, inputLength) {
const view = BufferSourceConverter.toUint8Array(inputBuffer);
if (!checkBufferParams(this, view, inputOffset, inputLength)) {
return -1;
}
const intBuffer = view.subarray(inputOffset, inputOffset + inputLength);
if (intBuffer.length === 0) {
this.error = "Zero buffer length";
return -1;
}
if (intBuffer[0] === 0xFF) {
this.error = "Length block 0xFF is reserved by standard";
return -1;
}
this.isIndefiniteForm = intBuffer[0] === 0x80;
if (this.isIndefiniteForm) {
this.blockLength = 1;
return inputOffset + this.blockLength;
}
this.longFormUsed = !!(intBuffer[0] & 0x80);
if (this.longFormUsed === false) {
this.length = intBuffer[0];
this.blockLength = 1;
return inputOffset + this.blockLength;
}
const count = intBuffer[0] & 0x7F;
if (count > 8) {
this.error = "Too big integer";
return -1;
}
if (count + 1 > intBuffer.length) {
this.error = "End of input reached before message was fully decoded";
return -1;
}
const lenOffset = inputOffset + 1;
const lengthBufferView = view.subarray(lenOffset, lenOffset + count);
if (lengthBufferView[count - 1] === 0x00) this.warnings.push("Needlessly long encoded length");
this.length = utilFromBase(lengthBufferView, 8);
if (this.longFormUsed && this.length <= 127) this.warnings.push("Unnecessary usage of long length form");
this.blockLength = count + 1;
return inputOffset + this.blockLength;
}
toBER(sizeOnly = false) {
let retBuf;
let retView;
if (this.length > 127) this.longFormUsed = true;
if (this.isIndefiniteForm) {
retBuf = new ArrayBuffer(1);
if (sizeOnly === false) {
retView = new Uint8Array(retBuf);
retView[0] = 0x80;
}
return retBuf;
}
if (this.longFormUsed) {
const encodedBuf = utilToBase(this.length, 8);
if (encodedBuf.byteLength > 127) {
this.error = "Too big length";
return EMPTY_BUFFER;
}
retBuf = new ArrayBuffer(encodedBuf.byteLength + 1);
if (sizeOnly) return retBuf;
const encodedView = new Uint8Array(encodedBuf);
retView = new Uint8Array(retBuf);
retView[0] = encodedBuf.byteLength | 0x80;
for (let i = 0; i < encodedBuf.byteLength; i++) retView[i + 1] = encodedView[i];
return retBuf;
}
retBuf = new ArrayBuffer(1);
if (sizeOnly === false) {
retView = new Uint8Array(retBuf);
retView[0] = this.length;
}
return retBuf;
}
toJSON() {
return {
...super.toJSON(),
isIndefiniteForm: this.isIndefiniteForm,
longFormUsed: this.longFormUsed,
length: this.length
};
}
}
LocalLengthBlock.NAME = "lengthBlock";
const typeStore = {};
class BaseBlock extends LocalBaseBlock {
constructor({
name = EMPTY_STRING,
optional = false,
primitiveSchema,
...parameters
} = {}, valueBlockType) {
super(parameters);
this.name = name;
this.optional = optional;
if (primitiveSchema) {
this.primitiveSchema = primitiveSchema;
}
this.idBlock = new LocalIdentificationBlock(parameters);
this.lenBlock = new LocalLengthBlock(parameters);
this.valueBlock = valueBlockType ? new valueBlockType(parameters) : new ValueBlock(parameters);
}
fromBER(inputBuffer, inputOffset, inputLength) {
const resultOffset = this.valueBlock.fromBER(inputBuffer, inputOffset, this.lenBlock.isIndefiniteForm ? inputLength : this.lenBlock.length);
if (resultOffset === -1) {
this.error = this.valueBlock.error;
return resultOffset;
}
if (!this.idBlock.error.length) this.blockLength += this.idBlock.blockLength;
if (!this.lenBlock.error.length) this.blockLength += this.lenBlock.blockLength;
if (!this.valueBlock.error.length) this.blockLength += this.valueBlock.blockLength;
return resultOffset;
}
toBER(sizeOnly, writer) {
const _writer = writer || new ViewWriter();
if (!writer) {
prepareIndefiniteForm(this);
}
const idBlockBuf = this.idBlock.toBER(sizeOnly);
_writer.write(idBlockBuf);
if (this.lenBlock.isIndefiniteForm) {
_writer.write(new Uint8Array([0x80]).buffer);
this.valueBlock.toBER(sizeOnly, _writer);
_writer.write(new ArrayBuffer(2));
} else {
const valueBlockBuf = this.valueBlock.toBER(sizeOnly);
this.lenBlock.length = valueBlockBuf.byteLength;
const lenBlockBuf = this.lenBlock.toBER(sizeOnly);
_writer.write(lenBlockBuf);
_writer.write(valueBlockBuf);
}
if (!writer) {
return _writer.final();
}
return EMPTY_BUFFER;
}
toJSON() {
const object = {
...super.toJSON(),
idBlock: this.idBlock.toJSON(),
lenBlock: this.lenBlock.toJSON(),
valueBlock: this.valueBlock.toJSON(),
name: this.name,
optional: this.optional
};
if (this.primitiveSchema) object.primitiveSchema = this.primitiveSchema.toJSON();
return object;
}
toString(encoding = "ascii") {
if (encoding === "ascii") {
return this.onAsciiEncoding();
}
return Convert.ToHex(this.toBER());
}
onAsciiEncoding() {
return `${this.constructor.NAME} : ${Convert.ToHex(this.valueBlock.valueBeforeDecodeView)}`;
}
isEqual(other) {
if (this === other) {
return true;
}
if (!(other instanceof this.constructor)) {
return false;
}
const thisRaw = this.toBER();
const otherRaw = other.toBER();
return isEqualBuffer(thisRaw, otherRaw);
}
}
BaseBlock.NAME = "BaseBlock";
function prepareIndefiniteForm(baseBlock) {
if (baseBlock instanceof typeStore.Constructed) {
for (const value of baseBlock.valueBlock.value) {
if (prepareIndefiniteForm(value)) {
baseBlock.lenBlock.isIndefiniteForm = true;
}
}
}
return !!baseBlock.lenBlock.isIndefiniteForm;
}
class BaseStringBlock extends BaseBlock {
constructor({
value = EMPTY_STRING,
...parameters
} = {}, stringValueBlockType) {
super(parameters, stringValueBlockType);
if (value) {
this.fromString(value);
}
}
getValue() {
return this.valueBlock.value;
}
setValue(value) {
this.valueBlock.value = value;
}
fromBER(inputBuffer, inputOffset, inputLength) {
const resultOffset = this.valueBlock.fromBER(inputBuffer, inputOffset, this.lenBlock.isIndefiniteForm ? inputLength : this.lenBlock.length);
if (resultOffset === -1) {
this.error = this.valueBlock.error;
return resultOffset;
}
this.fromBuffer(this.valueBlock.valueHexView);
if (!this.idBlock.error.length) this.blockLength += this.idBlock.blockLength;
if (!this.lenBlock.error.length) this.blockLength += this.lenBlock.blockLength;
if (!this.valueBlock.error.length) this.blockLength += this.valueBlock.blockLength;
return resultOffset;
}
onAsciiEncoding() {
return `${this.constructor.NAME} : '${this.valueBlock.value}'`;
}
}
BaseStringBlock.NAME = "BaseStringBlock";
class LocalPrimitiveValueBlock extends HexBlock(ValueBlock) {
constructor({
isHexOnly = true,
...parameters
} = {}) {
super(parameters);
this.isHexOnly = isHexOnly;
}
}
LocalPrimitiveValueBlock.NAME = "PrimitiveValueBlock";
var _a$w;
class Primitive extends BaseBlock {
constructor(parameters = {}) {
super(parameters, LocalPrimitiveValueBlock);
this.idBlock.isConstructed = false;
}
}
_a$w = Primitive;
(() => {
typeStore.Primitive = _a$w;
})();
Primitive.NAME = "PRIMITIVE";
function localChangeType(inputObject, newType) {
if (inputObject instanceof newType) {
return inputObject;
}
const newObject = new newType();
newObject.idBlock = inputObject.idBlock;
newObject.lenBlock = inputObject.lenBlock;
newObject.warnings = inputObject.warnings;
newObject.valueBeforeDecodeView = inputObject.valueBeforeDecodeView;
return newObject;
}
function localFromBER(inputBuffer, inputOffset = 0, inputLength = inputBuffer.length) {
const incomingOffset = inputOffset;
let returnObject = new BaseBlock({}, ValueBlock);
const baseBlock = new LocalBaseBlock();
if (!checkBufferParams(baseBlock, inputBuffer, inputOffset, inputLength)) {
returnObject.error = baseBlock.error;
return {
offset: -1,
result: returnObject
};
}
const intBuffer = inputBuffer.subarray(inputOffset, inputOffset + inputLength);
if (!intBuffer.length) {
returnObject.error = "Zero buffer length";
return {
offset: -1,
result: returnObject
};
}
let resultOffset = returnObject.idBlock.fromBER(inputBuffer, inputOffset, inputLength);
if (returnObject.idBlock.warnings.length) {
returnObject.warnings.concat(returnObject.idBlock.warnings);
}
if (resultOffset === -1) {
returnObject.error = returnObject.idBlock.error;
return {
offset: -1,
result: returnObject
};
}
inputOffset = resultOffset;
inputLength -= returnObject.idBlock.blockLength;
resultOffset = returnObject.lenBlock.fromBER(inputBuffer, inputOffset, inputLength);
if (returnObject.lenBlock.warnings.length) {
returnObject.warnings.concat(returnObject.lenBlock.warnings);
}
if (resultOffset === -1) {
returnObject.error = returnObject.lenBlock.error;
return {
offset: -1,
result: returnObject
};
}
inputOffset = resultOffset;
inputLength -= returnObject.lenBlock.blockLength;
if (!returnObject.idBlock.isConstructed && returnObject.lenBlock.isIndefiniteForm) {
returnObject.error = "Indefinite length form used for primitive encoding form";
return {
offset: -1,
result: returnObject
};
}
let newASN1Type = BaseBlock;
switch (returnObject.idBlock.tagClass) {
case 1:
if (returnObject.idBlock.tagNumber >= 37 && returnObject.idBlock.isHexOnly === false) {
returnObject.error = "UNIVERSAL 37 and upper tags are reserved by ASN.1 standard";
return {
offset: -1,
result: returnObject
};
}
switch (returnObject.idBlock.tagNumber) {
case 0:
if (returnObject.idBlock.isConstructed && returnObject.lenBlock.length > 0) {
returnObject.error = "Type [UNIVERSAL 0] is reserved";
return {
offset: -1,
result: returnObject
};
}
newASN1Type = typeStore.EndOfContent;
break;
case 1:
newASN1Type = typeStore.Boolean;
break;
case 2:
newASN1Type = typeStore.Integer;
break;
case 3:
newASN1Type = typeStore.BitString;
break;
case 4:
newASN1Type = typeStore.OctetString;
break;
case 5:
newASN1Type = typeStore.Null;
break;
case 6:
newASN1Type = typeStore.ObjectIdentifier;
break;
case 10:
newASN1Type = typeStore.Enumerated;
break;
case 12:
newASN1Type = typeStore.Utf8String;
break;
case 13:
newASN1Type = typeStore.RelativeObjectIdentifier;
break;
case 14:
newASN1Type = typeStore.TIME;
break;
case 15:
returnObject.error = "[UNIVERSAL 15] is reserved by ASN.1 standard";
return {
offset: -1,
result: returnObject
};
case 16:
newASN1Type = typeStore.Sequence;
break;
case 17:
newASN1Type = typeStore.Set;
break;
case 18:
newASN1Type = typeStore.NumericString;
break;
case 19:
newASN1Type = typeStore.PrintableString;
break;
case 20:
newASN1Type = typeStore.TeletexString;
break;
case 21:
newASN1Type = typeStore.VideotexString;
break;
case 22:
newASN1Type = typeStore.IA5String;
break;
case 23:
newASN1Type = typeStore.UTCTime;
break;
case 24:
newASN1Type = typeStore.GeneralizedTime;
break;
case 25:
newASN1Type = typeStore.GraphicString;
break;
case 26:
newASN1Type = typeStore.VisibleString;
break;
case 27:
newASN1Type = typeStore.GeneralString;
break;
case 28:
newASN1Type = typeStore.UniversalString;
break;
case 29:
newASN1Type = typeStore.CharacterString;
break;
case 30:
newASN1Type = typeStore.BmpString;
break;
case 31:
newASN1Type = typeStore.DATE;
break;
case 32:
newASN1Type = typeStore.TimeOfDay;
break;
case 33:
newASN1Type = typeStore.DateTime;
break;
case 34:
newASN1Type = typeStore.Duration;
break;
default:
{
const newObject = returnObject.idBlock.isConstructed ? new typeStore.Constructed() : new typeStore.Primitive();
newObject.idBlock = returnObject.idBlock;
newObject.lenBlock = returnObject.lenBlock;
newObject.warnings = returnObject.warnings;
returnObject = newObject;
}
}
break;
case 2:
case 3:
case 4:
default:
{
newASN1Type = returnObject.idBlock.isConstructed ? typeStore.Constructed : typeStore.Primitive;
}
}
returnObject = localChangeType(returnObject, newASN1Type);
resultOffset = returnObject.fromBER(inputBuffer, inputOffset, returnObject.lenBlock.isIndefiniteForm ? inputLength : returnObject.lenBlock.length);
returnObject.valueBeforeDecodeView = inputBuffer.subarray(incomingOffset, incomingOffset + returnObject.blockLength);
return {
offset: resultOffset,
result: returnObject
};
}
function fromBER(inputBuffer) {
if (!inputBuffer.byteLength) {
const result = new BaseBlock({}, ValueBlock);
result.error = "Input buffer has zero length";
return {
offset: -1,
result
};
}
return localFromBER(BufferSourceConverter.toUint8Array(inputBuffer).slice(), 0, inputBuffer.byteLength);
}
function checkLen(indefiniteLength, length) {
if (indefiniteLength) {
return 1;
}
return length;
}
class LocalConstructedValueBlock extends ValueBlock {
constructor({
value = [],
isIndefiniteForm = false,
...parameters
} = {}) {
super(parameters);
this.value = value;
this.isIndefiniteForm = isIndefiniteForm;
}
fromBER(inputBuffer, inputOffset, inputLength) {
const view = BufferSourceConverter.toUint8Array(inputBuffer);
if (!checkBufferParams(this, view, inputOffset, inputLength)) {
return -1;
}
this.valueBeforeDecodeView = view.subarray(inputOffset, inputOffset + inputLength);
if (this.valueBeforeDecodeView.length === 0) {
this.warnings.push("Zero buffer length");
return inputOffset;
}
let currentOffset = inputOffset;
while (checkLen(this.isIndefiniteForm, inputLength) > 0) {
const returnObject = localFromBER(view, currentOffset, inputLength);
if (returnObject.offset === -1) {
this.error = returnObject.result.error;
this.warnings.concat(returnObject.result.warnings);
return -1;
}
currentOffset = returnObject.offset;
this.blockLength += returnObject.result.blockLength;
inputLength -= returnObject.result.blockLength;
this.value.push(returnObject.result);
if (this.isIndefiniteForm && returnObject.result.constructor.NAME === END_OF_CONTENT_NAME) {
break;
}
}
if (this.isIndefiniteForm) {
if (this.value[this.value.length - 1].constructor.NAME === END_OF_CONTENT_NAME) {
this.value.pop();
} else {
this.warnings.push("No EndOfContent block encoded");
}
}
return currentOffset;
}
toBER(sizeOnly, writer) {
const _writer = writer || new ViewWriter();
for (let i = 0; i < this.value.length; i++) {
this.value[i].toBER(sizeOnly, _writer);
}
if (!writer) {
return _writer.final();
}
return EMPTY_BUFFER;
}
toJSON() {
const object = {
...super.toJSON(),
isIndefiniteForm: this.isIndefiniteForm,
value: []
};
for (const value of this.value) {
object.value.push(value.toJSON());
}
return object;
}
}
LocalConstructedValueBlock.NAME = "ConstructedValueBlock";
var _a$v;
class Constructed extends BaseBlock {
constructor(parameters = {}) {
super(parameters, LocalConstructedValueBlock);
this.idBlock.isConstructed = true;
}
fromBER(inputBuffer, inputOffset, inputLength) {
this.valueBlock.isIndefiniteForm = this.lenBlock.isIndefiniteForm;
const resultOffset = this.valueBlock.fromBER(inputBuffer, inputOffset, this.lenBlock.isIndefiniteForm ? inputLength : this.lenBlock.length);
if (resultOffset === -1) {
this.error = this.valueBlock.error;
return resultOffset;
}
if (!this.idBlock.error.length) this.blockLength += this.idBlock.blockLength;
if (!this.lenBlock.error.length) this.blockLength += this.lenBlock.blockLength;
if (!this.valueBlock.error.length) this.blockLength += this.valueBlock.blockLength;
return resultOffset;
}
onAsciiEncoding() {
const values = [];
for (const value of this.valueBlock.value) {
values.push(value.toString("ascii").split("\n").map(o => ` ${o}`).join("\n"));
}
const blockName = this.idBlock.tagClass === 3 ? `[${this.idBlock.tagNumber}]` : this.constructor.NAME;
return values.length ? `${blockName} :\n${values.join("\n")}` : `${blockName} :`;
}
}
_a$v = Constructed;
(() => {
typeStore.Constructed = _a$v;
})();
Constructed.NAME = "CONSTRUCTED";
class LocalEndOfContentValueBlock extends ValueBlock {
fromBER(inputBuffer, inputOffset, inputLength) {
return inputOffset;
}
toBER(sizeOnly) {
return EMPTY_BUFFER;
}
}
LocalEndOfContentValueBlock.override = "EndOfContentValueBlock";
var _a$u;
class EndOfContent extends BaseBlock {
constructor(parameters = {}) {
super(parameters, LocalEndOfContentValueBlock);
this.idBlock.tagClass = 1;
this.idBlock.tagNumber = 0;
}
}
_a$u = EndOfContent;
(() => {
typeStore.EndOfContent = _a$u;
})();
EndOfContent.NAME = END_OF_CONTENT_NAME;
var _a$t;
class Null extends BaseBlock {
constructor(parameters = {}) {
super(parameters, ValueBlock);
this.idBlock.tagClass = 1;
this.idBlock.tagNumber = 5;
}
fromBER(inputBuffer, inputOffset, inputLength) {
if (this.lenBlock.length > 0) this.warnings.push("Non-zero length of value block for Null type");
if (!this.idBlock.error.length) this.blockLength += this.idBlock.blockLength;
if (!this.lenBlock.error.length) this.blockLength += this.lenBlock.blockLength;
this.blockLength += inputLength;
if (inputOffset + inputLength > inputBuffer.byteLength) {
this.error = "End of input reached before message was fully decoded (inconsistent offset and length values)";
return -1;
}
return inputOffset + inputLength;
}
toBER(sizeOnly, writer) {
const retBuf = new ArrayBuffer(2);
if (!sizeOnly) {
const retView = new Uint8Array(retBuf);
retView[0] = 0x05;
retView[1] = 0x00;
}
if (writer) {
writer.write(retBuf);
}
return retBuf;
}
onAsciiEncoding() {
return `${this.constructor.NAME}`;
}
}
_a$t = Null;
(() => {
typeStore.Null = _a$t;
})();
Null.NAME = "NULL";
class LocalBooleanValueBlock extends HexBlock(ValueBlock) {
constructor({
value,
...parameters
} = {}) {
super(parameters);
if (parameters.valueHex) {
this.valueHexView = BufferSourceConverter.toUint8Array(parameters.valueHex);
} else {
this.valueHexView = new Uint8Array(1);
}
if (value) {
this.value = value;
}
}
get value() {
for (const octet of this.valueHexView) {
if (octet > 0) {
return true;
}
}
return false;
}
set value(value) {
this.valueHexView[0] = value ? 0xFF : 0x00;
}
fromBER(inputBuffer, inputOffset, inputLength) {
const inputView = BufferSourceConverter.toUint8Array(inputBuffer);
if (!che