"use strict";
//
//  Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.DerWriter = exports.DerReader = void 0;
const buffer_1 = require("buffer");
const bigInt_1 = require("./bigInt");
/**
 * Reads data in DER (Distinguished Encoding Rules) format.
 *
 * Enables importing and exporting key files, which are commonly DER-encoded.
 */
class DerReader {
    constructor(buffer, dataType = 32 /* DerType.Constructed */ | 16 /* DerType.Sequence */) {
        this.buffer = buffer;
        this.position = 0;
        this.readType(dataType);
        const length = this.readLength();
        if (length > this.buffer.length - this.position) {
            throw new Error('Read out of bounds.');
        }
        this.buffer = this.buffer.slice(0, this.position + length);
    }
    get available() {
        return this.buffer.length - this.position;
    }
    readNull() {
        this.readType(5 /* DerType.Null */);
        if (this.readByte() !== 0) {
            throw new Error('Expected a 0 after Null type.');
        }
    }
    readInteger() {
        this.readType(2 /* DerType.Integer */);
        const length = this.readLength();
        const bytes = this.readBytes(length);
        const result = new bigInt_1.BigInt(bytes);
        return result;
    }
    readOctetString() {
        this.readType(4 /* DerType.OctetString */);
        const length = this.readLength();
        const result = this.readBytes(length);
        return result;
    }
    readBitString() {
        this.readType(3 /* DerType.BitString */);
        const length = this.readLength();
        const padding = this.readByte();
        if (padding !== 0) {
            throw new Error('Padded bit strings are not supported.');
        }
        const result = this.readBytes(length - 1);
        return result;
    }
    readObjectIdentifier(expected) {
        this.readType(6 /* DerType.ObjectIdentifier */);
        const length = this.readLength();
        const end = this.position + length;
        const values = [];
        const first = this.readByte();
        values.push(Math.trunc(first / 40));
        values.push(first % 40);
        let next = 0;
        while (this.position < end) {
            const b = this.readByte();
            if ((b & 0x80) !== 0) {
                next = next * 128 + (b & 0x7f);
            }
            else {
                next = next * 128 + b;
                values.push(next);
                next = 0;
            }
        }
        if (next !== 0) {
            throw new Error('Invalid OID format.');
        }
        const result = values.join('.');
        if (expected && result !== expected) {
            throw new Error(`Expected OID ${expected}, found: ${result}`);
        }
        return result;
    }
    readSequence() {
        const start = this.position;
        this.readType(32 /* DerType.Constructed */ | 16 /* DerType.Sequence */);
        const length = this.readLength();
        this.position += length;
        return new DerReader(this.buffer.slice(start, this.position));
    }
    tryReadTagged(tagId) {
        if (this.position >= this.buffer.length) {
            return null;
        }
        const type = this.buffer[this.position];
        if ((type & 160 /* DerType.Tagged */) === 0 || (type & ~160 /* DerType.Tagged */) !== tagId) {
            return null;
        }
        const start = this.position;
        this.position++;
        const length = this.readLength();
        this.position += length;
        const taggedData = new DerReader(this.buffer.slice(start, this.position), type);
        return taggedData;
    }
    /** Reads the type of the next value in the sequence WITHOUT advancing the reader position. */
    peek() {
        if (this.position >= this.buffer.length) {
            throw new Error('Read out of bounds.');
        }
        return this.buffer[this.position];
    }
    readLength() {
        let length = this.readByte();
        if (length === 0x80) {
            throw new Error('Indefinite-length encoding is not supported.');
        }
        if (length > 127) {
            const size = length & 0x7f;
            if (size > 4) {
                throw new Error(`DER length size is ${size} and cannot be more than 4 bytes.`);
            }
            length = 0;
            for (let i = 0; i < size; i++) {
                const next = this.readByte();
                length = (length << 8) + next;
            }
            if (length < 0) {
                throw new Error('Corrupted data - negative length found');
            }
        }
        return length;
    }
    readByte() {
        if (this.position >= this.buffer.length) {
            throw new Error('Read out of bounds.');
        }
        return this.buffer[this.position++];
    }
    readBytes(length) {
        if (this.position + length > this.buffer.length) {
            throw new Error('Read out of bounds.');
        }
        const result = this.buffer.slice(this.position, this.position + length);
        this.position += length;
        return result;
    }
    readType(expectedType) {
        const type = this.readByte();
        if (type !== expectedType) {
            throw new Error(`Expected ${expectedType} data type, found : ${type}`);
        }
    }
}
exports.DerReader = DerReader;
/**
 * Writes data in DER (Distinguished Encoding Rules) format.
 *
 * Enables importing and exporting key files, which are commonly DER-encoded.
 */
class DerWriter {
    constructor(buffer, dataType = 32 /* DerType.Constructed */ | 16 /* DerType.Sequence */) {
        this.buffer = buffer;
        this.dataType = dataType;
        this.position = 0;
        this.buffer = buffer;
    }
    toBuffer() {
        // Move the data over to make space for the type + length prefix.
        const length = this.position;
        const lengthBytes = DerWriter.getLength(length);
        this.ensureCapacity(1 + lengthBytes.length + length);
        const result = this.buffer.slice(0, 1 + lengthBytes.length + length);
        this.buffer.copy(result, 1 + lengthBytes.length);
        // Write the type + length prefix.
        result[0] = this.dataType;
        lengthBytes.copy(result, 1, 0);
        // Restore the writer buffer to its previous state (without the type + length prefix).
        this.buffer = this.buffer.slice(1 + lengthBytes.length, result.length);
        this.position = length;
        return result;
    }
    writeSequence(data) {
        this.writeBytes(data.toBuffer());
    }
    writeTagged(tagId, data) {
        if (tagId > 0xf)
            throw new Error('Invalid DER tag.');
        this.writeByte(160 /* DerType.Tagged */ | tagId);
        const lengthBytes = DerWriter.getLength(data.position);
        this.writeBytes(lengthBytes);
        this.writeBytes(data.buffer.slice(0, data.position));
    }
    writeNull() {
        this.writeByte(5 /* DerType.Null */);
        this.writeByte(0);
    }
    writeInteger(value) {
        this.writeByte(2 /* DerType.Integer */);
        const integerBytes = value.toBytes();
        const lengthBytes = DerWriter.getLength(integerBytes.length);
        this.writeBytes(lengthBytes);
        this.writeBytes(integerBytes);
    }
    writeOctetString(data) {
        this.writeByte(4 /* DerType.OctetString */);
        const lengthBytes = DerWriter.getLength(data.length);
        this.writeBytes(lengthBytes);
        this.writeBytes(data);
    }
    writeBitString(data) {
        this.writeByte(3 /* DerType.BitString */);
        const lengthBytes = DerWriter.getLength(1 + data.length);
        this.writeBytes(lengthBytes);
        this.writeByte(0);
        this.writeBytes(data);
    }
    writeObjectIdentifier(oid) {
        if (!oid)
            throw new TypeError('OID value is null or empty.');
        const values = oid.split('.').map(Number);
        if (values.length < 2 || values[0] > 3 || values[1] >= 40) {
            throw new Error(`Invalid OID: ${oid}`);
        }
        this.writeByte(6 /* DerType.ObjectIdentifier */);
        let length = values.length - 1;
        for (let i = 2; i < values.length; i++) {
            let value = values[i];
            while (value > 128) {
                length++;
                value /= 128;
            }
        }
        const lengthBytes = DerWriter.getLength(length);
        this.writeBytes(lengthBytes);
        this.writeByte(values[0] * 40 + values[1]);
        for (let i = 2; i < values.length; i++) {
            let value = values[i];
            if (value >= 128) {
                const bytes = [];
                bytes.push(value & 0x7f);
                while (value >= 128) {
                    value /= 128;
                    bytes.push(0x80 | (value & 0x7f));
                }
                while (bytes.length > 0) {
                    this.writeByte(bytes.pop());
                }
            }
            else {
                this.writeByte(value);
            }
        }
    }
    static getLength(length) {
        if (length > 127) {
            let size = 1;
            for (let val = length >> 8; val !== 0; val >>= 8) {
                size++;
            }
            const lengthBytes = DerWriter.lengthBuffer.slice(0, size + 1);
            lengthBytes[0] = size | 0x80;
            for (let i = (size - 1) * 8, j = 1; i >= 0; i -= 8, j++) {
                lengthBytes[j] = length >> i;
            }
            return lengthBytes;
        }
        else {
            const lengthBytes = DerWriter.lengthBuffer.slice(0, 1);
            lengthBytes[0] = length;
            return lengthBytes;
        }
    }
    writeByte(value) {
        this.ensureCapacity(this.position + 1);
        this.buffer[this.position++] = value;
    }
    writeBytes(value) {
        this.ensureCapacity(this.position + value.length);
        value.copy(this.buffer, this.position);
        this.position += value.length;
    }
    ensureCapacity(capacity) {
        if (this.buffer.length < capacity) {
            let newLength = Math.max(512, this.buffer.length * 2);
            while (newLength < capacity)
                newLength *= 2;
            const newBuffer = buffer_1.Buffer.alloc(newLength);
            this.buffer.copy(newBuffer, 0, 0, this.position);
            this.buffer = newBuffer;
        }
    }
}
exports.DerWriter = DerWriter;
DerWriter.lengthBuffer = buffer_1.Buffer.alloc(10);
//# sourceMappingURL=https://main.vscode-cdn.net/sourcemaps/6a44c352bd24569c417e530095901b649960f9f8/node_modules/@microsoft/dev-tunnels-ssh/io/derData.js.map