"use strict";
//
//  Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.SshSessionConfiguration = exports.SshProtocolExtensionNames = void 0;
const vscode_jsonrpc_1 = require("vscode-jsonrpc");
const sshAlgorithms_1 = require("./algorithms/sshAlgorithms");
const keyExchangeService_1 = require("./services/keyExchangeService");
const connectionService_1 = require("./services/connectionService");
const authenticationService_1 = require("./services/authenticationService");
const sshMessage_1 = require("./messages/sshMessage");
var SshProtocolExtensionNames;
(function (SshProtocolExtensionNames) {
    /**
     * Lists host key signature algorithms enabled by the sender.
     *
     * This is a "standard" protocol extension supported by most SSH implementations.
     */
    SshProtocolExtensionNames["serverSignatureAlgorithms"] = "server-sig-algs";
    /**
     * An optimization that enables sending an initial channel request without
     * waiting for a channel open confirmation message.
     */
    SshProtocolExtensionNames["openChannelRequest"] = "open-channel-request@microsoft.com";
    /**
     * Enables reconnecting to a session that was recently disconnected.
     */
    SshProtocolExtensionNames["sessionReconnect"] = "session-reconnect@microsoft.com";
    /**
     * Enables continual latency measurements between client and server.
     *
     * This extension requires that the reconnect extension is also enabled, because
     * it leverages some of the session history info for reconnect to compute latency.
     */
    SshProtocolExtensionNames["sessionLatency"] = "session-latency@microsoft.com";
})(SshProtocolExtensionNames = exports.SshProtocolExtensionNames || (exports.SshProtocolExtensionNames = {}));
/**
 * Specifies the sets of algorithms and other configuration for an SSH session.
 *
 * Each collection of algorithms is in order of preference. Server and client
 * negotiate the most-preferred algorithm that is supported by both.
 */
class SshSessionConfiguration {
    constructor(useSecurity = true) {
        /**
         * Gets the protocol extensions that are enabled for the session.
         */
        this.protocolExtensions = [];
        /**
         * Gets a dictionary that maps from service types to service configuration objects.
         *
         * Service types must extend the `SshService` abstract class.
         *
         * The service configuration object is passed to the service constructor upon activation.
         */
        this.services = new Map();
        /**
         * Gets the list of enabled authentication methods.
         *
         * Add or remove `AuthenticationMethod` constants to restrict which client authentication
         * methods the client will try or the server will allow. In any case, the client or server must
         * handle the `SshSession.onAuthenticating` event to perform authentication.
         */
        this.authenticationMethods = [];
        /**
         * Gets a dictionary that maps from known message numbers to message types.
         *
         * Message types must extend the `SshMessage` abstract class. Message subclasses that do
         * not have a distinct message type from their base class must not be included in this map.
         */
        this.messages = new Map();
        /**
         * Gets a dictionary that maps from message context to message type mappings for each context.
         *
         * Services like `AuthenticationService` may set the current message context to
         * disambiguate when the same message number may be re-used in different contexts.
         */
        this.contextualMessages = new Map();
        /**
         * Gets the collection of algorithms that are enabled for key exchange.
         *
         * Client and server sides negotiate which of these algorithms will be used.
         *
         * If this collection includes `null`, and if negotiation selects it, then the session is
         * allowed to skip key exchange and connect with no security of any kind: No key exchange,
         * no authentication, no encryption, no HMAC, and no compression.
         */
        this.keyExchangeAlgorithms = [];
        /**
         * Gets the collection of algorithms that are enabled for server (host) and client
         * public-key authentication.
         *
         * Client and server sides negotiate which of these algorithms will be used.
         */
        this.publicKeyAlgorithms = [];
        /*
         * Gets the collection of algorithms that are enabled for encryption.
         *
         * Client and server sides negotiate which of these algorithms will be used.
         */
        this.encryptionAlgorithms = [];
        /**
         * Gets the collection of algorithms that are enabled for message integrity (HMAC).
         *
         * Client and server sides negotiate which of these algorithms will be used.
         */
        this.hmacAlgorithms = [];
        /**
         * Gets the collection of algorithms that are enabled for message compression.
         *
         * Client and server sides negotiate which of these algorithms will be used.
         */
        this.compressionAlgorithms = [];
        this.traceChannelDataValue = false;
        this.configurationChangedEmitter = new vscode_jsonrpc_1.Emitter();
        /* @internal */
        this.onConfigurationChanged = this.configurationChangedEmitter.event;
        /**
         * Gets or sets the number of times the server will allow a client to attempt to
         * authenticate.
         *
         * The default value is 5.
         *
         * This setting applies only to server sessions. If the client has failed to authenticate
         * after the maximum number of atttempts, the server will close the session.
         *
         * The SSH protocol allows a client to make multiple attempts to authenticate with
         * the server, e.g. to find which public key algorithm a server will support, or to
         * retry a mis-typed password. This maximum prevents unlimited retries, which would
         * make it easier to "guess" a password.
         *
         * In certain applications the server may only support a single authentication method
         * (which is not a typed password). Then it could be appropriate to set this value to 1.
         */
        this.maxClientAuthenticationAttempts = 5;
        /**
         * Gets or sets whether the client sends a key-exchange "guess" message before receiving
         * the server's key-exchange algorithm preferences, slightly reducing the time to connect.
         *
         * This setting only applies to client sessions. (The server always supports the option when
         * used by a client.)
         *
         * The "guess" mechanism is somewhat ambiguously defined in the SSH protocol spec, and as
         * a result is not implemented or incorrectly implemented by some server implementations,
         * including older versions of this library. Therefore it is disabled in the default
         * configuration, and should only be enabled when connecting to a known-good server.
         */
        this.enableKeyExchangeGuess = false;
        /**
         * Gets or sets the keep-alive timeout in seconds.
         *
         * When set to a value greater than 0, the session will send keep-alive messages
         * at the specified interval to detect connection failures. If no response is received
         * within the timeout period, the keep-alive failed event will be raised.
         *
         * Set to 0 to disable keep-alive messages.
         */
        this.keepAliveTimeoutInSecondsValue = 0;
        /* @internal */
        this.keyRotationThreshold = 512 * 1024 * 1024; // 0.5 GiB;
        this.protocolExtensions.push(SshProtocolExtensionNames.serverSignatureAlgorithms);
        this.protocolExtensions.push(SshProtocolExtensionNames.openChannelRequest);
        this.services.set(keyExchangeService_1.KeyExchangeService, null);
        this.services.set(connectionService_1.ConnectionService, null);
        this.services.set(authenticationService_1.AuthenticationService, null);
        this.authenticationMethods.push("none" /* AuthenticationMethod.none */, "password" /* AuthenticationMethod.password */, "publickey" /* AuthenticationMethod.publicKey */, "keyboard-interactive" /* AuthenticationMethod.keyboardInteractive */);
        for (const [messageKey, messageType] of sshMessage_1.SshMessage.index) {
            if (typeof messageKey === 'number') {
                this.messages.set(messageKey, messageType);
            }
            else {
                const [messageNumber, messageContext] = messageKey;
                let contextMessageTypes = this.contextualMessages.get(messageContext);
                if (!contextMessageTypes) {
                    contextMessageTypes = new Map();
                    this.contextualMessages.set(messageContext, contextMessageTypes);
                }
                contextMessageTypes.set(messageNumber, messageType);
            }
        }
        if (useSecurity) {
            this.keyExchangeAlgorithms.push(sshAlgorithms_1.SshAlgorithms.keyExchange.ecdhNistp384Sha384);
            this.keyExchangeAlgorithms.push(sshAlgorithms_1.SshAlgorithms.keyExchange.ecdhNistp256Sha256);
            this.keyExchangeAlgorithms.push(sshAlgorithms_1.SshAlgorithms.keyExchange.dhGroup16Sha512);
            this.keyExchangeAlgorithms.push(sshAlgorithms_1.SshAlgorithms.keyExchange.dhGroup14Sha256);
            this.publicKeyAlgorithms.push(sshAlgorithms_1.SshAlgorithms.publicKey.rsaWithSha512);
            this.publicKeyAlgorithms.push(sshAlgorithms_1.SshAlgorithms.publicKey.rsaWithSha256);
            this.publicKeyAlgorithms.push(sshAlgorithms_1.SshAlgorithms.publicKey.ecdsaSha2Nistp384);
            this.publicKeyAlgorithms.push(sshAlgorithms_1.SshAlgorithms.publicKey.ecdsaSha2Nistp256);
            ////this.encryptionAlgorithms.push(SshAlgorithms.encryption.aes256Cbc);
            this.encryptionAlgorithms.push(sshAlgorithms_1.SshAlgorithms.encryption.aes256Gcm);
            this.encryptionAlgorithms.push(sshAlgorithms_1.SshAlgorithms.encryption.aes256Ctr);
            this.hmacAlgorithms.push(sshAlgorithms_1.SshAlgorithms.hmac.hmacSha512Etm);
            this.hmacAlgorithms.push(sshAlgorithms_1.SshAlgorithms.hmac.hmacSha256Etm);
            this.hmacAlgorithms.push(sshAlgorithms_1.SshAlgorithms.hmac.hmacSha512);
            this.hmacAlgorithms.push(sshAlgorithms_1.SshAlgorithms.hmac.hmacSha256);
        }
        else {
            this.keyExchangeAlgorithms.push(sshAlgorithms_1.SshAlgorithms.keyExchange.none);
            this.publicKeyAlgorithms.push(sshAlgorithms_1.SshAlgorithms.publicKey.none);
            this.encryptionAlgorithms.push(sshAlgorithms_1.SshAlgorithms.encryption.none);
            this.hmacAlgorithms.push(sshAlgorithms_1.SshAlgorithms.hmac.none);
        }
        this.compressionAlgorithms.push(sshAlgorithms_1.SshAlgorithms.compression.none);
    }
    /**
     * Adds a service to the configuration.
     */
    addService(serviceType, serviceConfig) {
        if (this.services.has(serviceType)) {
            throw new Error('Duplicate service entry.');
        }
        this.services.set(serviceType, serviceConfig);
    }
    getKeyExchangeAlgorithm(name) {
        return this.getAlgorithm(name, this.keyExchangeAlgorithms);
    }
    getPublicKeyAlgorithm(name) {
        return this.getAlgorithm(name, this.publicKeyAlgorithms);
    }
    getEncryptionAlgorithm(name) {
        return this.getAlgorithm(name, this.encryptionAlgorithms);
    }
    getHmacAlgorithm(name) {
        return this.getAlgorithm(name, this.hmacAlgorithms);
    }
    getCompressionAlgorithm(name) {
        return this.getAlgorithm(name, this.compressionAlgorithms);
    }
    getAlgorithm(name, collection) {
        const algorithm = collection.find((a) => (a ? a.name === name : false));
        if (!algorithm) {
            if (name === 'none') {
                return null;
            }
            throw new Error('Algorithm not found: ' + name);
        }
        return algorithm;
    }
    get traceChannelData() {
        return this.traceChannelDataValue;
    }
    /**
     * Enables tracing of all channel data messages.
     *
     * Unlike other configuration, this option may be adjusted any time while the session
     * is active. Channel data tracing produces a large volume of trace events, so it is
     * primarily meant only for debugging.
     */
    set traceChannelData(value) {
        if (value !== this.traceChannelDataValue) {
            this.traceChannelDataValue = value;
            this.configurationChangedEmitter.fire();
        }
    }
    get keepAliveTimeoutInSeconds() {
        return this.keepAliveTimeoutInSecondsValue;
    }
    set keepAliveTimeoutInSeconds(value) {
        if (this.keepAliveTimeoutInSecondsValue !== value) {
            this.keepAliveTimeoutInSecondsValue = value;
            this.configurationChangedEmitter.fire();
        }
    }
}
exports.SshSessionConfiguration = SshSessionConfiguration;
//# sourceMappingURL=https://main.vscode-cdn.net/sourcemaps/6a44c352bd24569c417e530095901b649960f9f8/node_modules/@microsoft/dev-tunnels-ssh/sshSessionConfiguration.js.map