"use strict";
//
//  Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeRsa = void 0;
const crypto = require("crypto");
const buffer_1 = require("buffer");
const publicKeyAlgorithm_1 = require("../publicKeyAlgorithm");
const sshData_1 = require("../../io/sshData");
const nodeHmac_1 = require("./nodeHmac");
const keyFormatters_1 = require("./keyFormatters");
const nodeVersionParts = process.versions.node.split('.').map((v) => parseInt(v, 10));
const nodeGenerateKeyPairSupport = nodeVersionParts[0] > 10 || (nodeVersionParts[0] === 10 && nodeVersionParts[1] >= 12);
const nodeKeyObjectSupport = nodeVersionParts[0] > 11 || (nodeVersionParts[0] === 11 && nodeVersionParts[1] >= 6);
// Note this is exposed as an inner-class property below: `NodeRsa.KeyPair`.
// TypeScript requires that the class definition comes first.
class NodeRsaKeyPair {
    /* @internal */
    constructor() {
        this.comment = null;
    }
    get hasPublicKey() {
        return !!this.publicKey;
    }
    get hasPrivateKey() {
        return !!this.privateKey;
    }
    get keyAlgorithmName() {
        return NodeRsa.keyAlgorithmName;
    }
    generate(keySizeInBits) {
        keySizeInBits = keySizeInBits !== null && keySizeInBits !== void 0 ? keySizeInBits : NodeRsaKeyPair.defaultKeySize;
        if (nodeGenerateKeyPairSupport && nodeKeyObjectSupport) {
            return this.generateNodeKeyPairObjects(keySizeInBits);
        }
        else if (nodeGenerateKeyPairSupport) {
            return this.generateNodeKeyPairBuffers(keySizeInBits);
        }
        else {
            return this.generateExternalKeyPair(keySizeInBits);
        }
    }
    async generateNodeKeyPairObjects(keySizeInBits) {
        [this.publicKey, this.privateKey] = await new Promise((resolve, reject) => {
            const keyGenParams = {
                modulusLength: keySizeInBits,
            };
            try {
                crypto.generateKeyPair('rsa', keyGenParams, (err, publicKey, privateKey) => {
                    if (err) {
                        reject(err);
                    }
                    else {
                        resolve([publicKey, privateKey]);
                    }
                });
            }
            catch (err) {
                reject(err);
            }
        });
    }
    async generateNodeKeyPairBuffers(keySizeInBits) {
        [this.publicKey, this.privateKey] = await new Promise((resolve, reject) => {
            const keyGenParams = {
                modulusLength: keySizeInBits,
                publicKeyEncoding: { type: 'pkcs1', format: 'pem' },
                privateKeyEncoding: {
                    type: 'pkcs1',
                    format: 'pem',
                    cipher: undefined,
                    passphrase: undefined,
                },
            };
            try {
                crypto.generateKeyPair('rsa', keyGenParams, (err, publicKey, privateKey) => {
                    if (err) {
                        reject(err);
                    }
                    else {
                        resolve([publicKey, privateKey]);
                    }
                });
            }
            catch (err) {
                reject(err);
            }
        });
    }
    async generateExternalKeyPair(keySizeInBits) {
        // When running in a version of node that doesn't have a built-in API
        // for RSA key-gen, use an external library. Note this implementation
        // is SLOW because it's pure JS. It may take 1-5 seconds to generate
        // a 2048 bit key.
        const externRsa = await Promise.resolve().then(() => require('node-rsa'));
        const keyPair = new externRsa({ b: keySizeInBits });
        this.publicKey = keyPair.exportKey('pkcs1-public-pem');
        this.privateKey = keyPair.exportKey('pkcs1-private-pem');
        // Ensure the PEM format ends in a newline, just for consistency.
        if (!this.publicKey.endsWith('\n'))
            this.publicKey += '\n';
        if (!this.privateKey.endsWith('\n'))
            this.privateKey += '\n';
    }
    async setPublicKeyBytes(keyBytes) {
        if (!keyBytes) {
            throw new TypeError('Buffer is required.');
        }
        // Read public key in SSH format.
        const reader = new sshData_1.SshDataReader(keyBytes);
        const algorithmName = reader.readString('ascii');
        if (algorithmName !== this.keyAlgorithmName &&
            algorithmName !== NodeRsa.rsaWithSha256 &&
            algorithmName !== NodeRsa.rsaWithSha512) {
            throw new Error(`Invalid RSA key algorithm: ${algorithmName}`);
        }
        const exponent = reader.readBigInt();
        const modulus = reader.readBigInt();
        // Write public key in PKCS#1 format.
        keyBytes = keyFormatters_1.Pkcs1KeyFormatter.formatRsaPublic({ modulus, exponent });
        if (nodeKeyObjectSupport) {
            this.publicKey = crypto.createPublicKey({
                key: keyBytes,
                type: 'pkcs1',
                format: 'der',
            });
        }
        else {
            this.publicKey = (0, keyFormatters_1.formatPem)(keyBytes, 'RSA PUBLIC KEY');
        }
    }
    async getPublicKeyBytes(algorithmName) {
        if (!this.publicKey) {
            return null;
        }
        if (!algorithmName) {
            algorithmName = this.keyAlgorithmName;
        }
        let keyBytes;
        if (typeof this.publicKey === 'string') {
            keyBytes = (0, keyFormatters_1.parsePem)(this.publicKey);
        }
        else {
            keyBytes = this.publicKey.export({
                type: 'pkcs1',
                format: 'der',
            });
        }
        const parameters = keyFormatters_1.Pkcs1KeyFormatter.parseRsaPublic(keyBytes);
        // Write public key in SSH format.
        const keyBuffer = buffer_1.Buffer.alloc(512);
        const keyWriter = new sshData_1.SshDataWriter(keyBuffer);
        keyWriter.writeString(algorithmName, 'ascii');
        keyWriter.writeBigInt(parameters.exponent);
        keyWriter.writeBigInt(parameters.modulus);
        keyBytes = keyWriter.toBuffer();
        return keyBytes;
    }
    async importParameters(parameters) {
        if (nodeKeyObjectSupport) {
            this.publicKey = crypto.createPublicKey({
                key: keyFormatters_1.Pkcs1KeyFormatter.formatRsaPublic(parameters),
                format: 'der',
                type: 'pkcs1',
            });
            if (parameters.d) {
                this.privateKey = crypto.createPrivateKey({
                    key: keyFormatters_1.Pkcs1KeyFormatter.formatRsaPrivate(parameters),
                    format: 'der',
                    type: 'pkcs1',
                });
            }
            else {
                this.privateKey = undefined;
            }
        }
        else {
            const publicKeyBytes = keyFormatters_1.Pkcs1KeyFormatter.formatRsaPublic(parameters);
            this.publicKey = (0, keyFormatters_1.formatPem)(publicKeyBytes, 'RSA PUBLIC KEY');
            if (parameters.d) {
                const privateKeyBytes = keyFormatters_1.Pkcs1KeyFormatter.formatRsaPrivate(parameters);
                this.privateKey = (0, keyFormatters_1.formatPem)(privateKeyBytes, 'RSA PRIVATE KEY');
            }
        }
    }
    async exportParameters() {
        var _a, _b;
        if (!this.publicKey)
            throw new Error('Public key not set.');
        let keyBytes;
        if (nodeKeyObjectSupport) {
            keyBytes = ((_a = this.privateKey) !== null && _a !== void 0 ? _a : this.publicKey).export({
                format: 'der',
                type: 'pkcs1',
            });
        }
        else {
            keyBytes = (0, keyFormatters_1.parsePem)((_b = this.privateKey) !== null && _b !== void 0 ? _b : this.publicKey);
        }
        return this.privateKey
            ? keyFormatters_1.Pkcs1KeyFormatter.parseRsaPrivate(keyBytes)
            : keyFormatters_1.Pkcs1KeyFormatter.parseRsaPublic(keyBytes);
    }
    dispose() {
        this.publicKey = undefined;
        this.privateKey = undefined;
    }
}
NodeRsaKeyPair.defaultKeySize = 2048;
class NodeRsa extends publicKeyAlgorithm_1.PublicKeyAlgorithm {
    constructor(name, hashAlgorithmName) {
        super(name, NodeRsa.keyAlgorithmName, hashAlgorithmName);
    }
    createKeyPair() {
        return new NodeRsaKeyPair();
    }
    async generateKeyPair(keySizeInBits) {
        const rsaKey = new NodeRsaKeyPair();
        await rsaKey.generate(keySizeInBits);
        return rsaKey;
    }
    createSigner(keyPair) {
        if (!(keyPair instanceof NodeRsaKeyPair)) {
            throw new TypeError('RSA key pair object expected.');
        }
        return new NodeRsaSignerVerifier(keyPair, NodeRsa.convertHashAlgorithmName(this.hashAlgorithmName), nodeHmac_1.NodeHmac.getHashDigestLength(this.hashAlgorithmName));
    }
    createVerifier(keyPair) {
        if (!(keyPair instanceof NodeRsaKeyPair)) {
            throw new TypeError('RSA key pair object expected.');
        }
        return new NodeRsaSignerVerifier(keyPair, NodeRsa.convertHashAlgorithmName(this.hashAlgorithmName), nodeHmac_1.NodeHmac.getHashDigestLength(this.hashAlgorithmName));
    }
    static convertHashAlgorithmName(hashAlgorithmName) {
        return hashAlgorithmName.replace('SHA2-', 'SHA');
    }
}
exports.NodeRsa = NodeRsa;
NodeRsa.keyAlgorithmName = 'ssh-rsa';
NodeRsa.rsaWithSha256 = 'rsa-sha2-256';
NodeRsa.rsaWithSha512 = 'rsa-sha2-512';
NodeRsa.KeyPair = NodeRsaKeyPair;
class NodeRsaSignerVerifier {
    constructor(keyPair, hashAlgorithmName, digestLength) {
        this.keyPair = keyPair;
        this.hashAlgorithmName = hashAlgorithmName;
        this.digestLength = digestLength;
    }
    async sign(data) {
        if (!this.keyPair.privateKey) {
            throw new Error('Private key not set.');
        }
        const signer = crypto.createSign(this.hashAlgorithmName);
        signer.update(data);
        const signature = signer.sign(this.keyPair.privateKey);
        return signature;
    }
    async verify(data, signature) {
        if (!this.keyPair.publicKey) {
            throw new Error('Public key not set.');
        }
        const verifier = crypto.createVerify(this.hashAlgorithmName);
        verifier.update(data);
        const result = verifier.verify(this.keyPair.publicKey, signature);
        return result;
    }
    dispose() { }
}
//# sourceMappingURL=https://github.com/VSCodium/sourcemaps/releases/download/stable-824c4c46a288b839f13b24022655329c2aeb9f81/node_modules-@microsoft-dev-tunnels-ssh-algorithms-node-nodeRsa.js.map