UNPKG

@microsoft/dev-tunnels-ssh

Version:
160 lines 6.09 kB
"use strict"; // // Copyright (c) Microsoft Corporation. All rights reserved. // Object.defineProperty(exports, "__esModule", { value: true }); exports.NodeEncryption = void 0; const crypto = require("crypto"); const buffer_1 = require("buffer"); const encryptionAlgorithm_1 = require("../encryptionAlgorithm"); class NodeEncryption extends encryptionAlgorithm_1.EncryptionAlgorithm { constructor(name, algorithmName, cipherMode, keySizeInBits) { super(name); this.algorithmName = algorithmName; this.cipherMode = cipherMode; this.keySizeInBits = keySizeInBits; if (algorithmName !== 'AES') { throw new Error(`Unsupported encryption algorithm: ${algorithmName}`); } this.blockSizeInBits = NodeEncryption.getBlockSize(algorithmName); } get keyLength() { return this.keySizeInBits / 8; } get blockLength() { return this.blockSizeInBits / 8; } async createCipher(isEncryption, key, iv) { let cipher; if (this.cipherMode === 'CTR' || this.cipherMode === 'CBC') { cipher = new NodeAesCipher(isEncryption, this.keySizeInBits, this.blockSizeInBits, key, iv, this.cipherMode); } else if (this.cipherMode === 'GCM') { cipher = new NodeAesGcmCipher(isEncryption, this.keySizeInBits, this.blockSizeInBits, key, iv); } else { throw new Error(`Unsupported cipher mode: ${this.cipherMode}`); } return cipher; } static getBlockSize(algorithmName) { if (algorithmName === 'AES') { return 128; } else { throw new Error(`Unsupported encryption algorithm: ${algorithmName}`); } } } exports.NodeEncryption = NodeEncryption; class NodeAesCipher { constructor(isEncryption, keySizeInBits, blockSizeInBits, key, iv, cipherMode) { this.isEncryption = isEncryption; this.keySizeInBits = keySizeInBits; this.blockSizeInBits = blockSizeInBits; const nodeAlgorithm = `AES-${this.keySizeInBits}-${cipherMode}`; this.cipher = this.isEncryption ? crypto.createCipheriv(nodeAlgorithm, key, iv) : crypto.createDecipheriv(nodeAlgorithm, key, iv); this.cipher.setAutoPadding(false); } get blockLength() { return this.blockSizeInBits / 8; } transform(data) { const result = this.cipher.update(data); if (result.length !== data.length) { const message = 'Result from encrypt/decrypt has invalid length ' + `${result.length}, expected ${data.length}.`; throw new Error(message); } return Promise.resolve(result); } dispose() { } } class NodeAesGcmCipher { constructor(isEncryption, keySizeInBits, blockSizeInBits, key, iv) { this.isEncryption = isEncryption; this.keySizeInBits = keySizeInBits; this.blockSizeInBits = blockSizeInBits; this.tag = null; this.algorithmName = `aes-${this.keySizeInBits}-gcm`; this.key = buffer_1.Buffer.alloc(key.length); key.copy(this.key); // Ininitialize the nonce to the first 12 bytes of the IV. It will be incremented by each op. this.nonce = buffer_1.Buffer.alloc(12); iv.copy(this.nonce, 0, 0, 12); this.associatedData = buffer_1.Buffer.alloc(4); } get blockLength() { return this.blockSizeInBits / 8; } get digestLength() { return 16; } get authenticatedEncryption() { return true; } transform(data) { if (data.length % this.blockLength !== 0) { const message = 'Encrypt/decrypt input has invalid length ' + `${data.length}, not a multiple of block size ${this.blockLength}.`; throw new Error(message); } const cipher = this.isEncryption ? crypto.createCipheriv(this.algorithmName, this.key, this.nonce) : crypto.createDecipheriv(this.algorithmName, this.key, this.nonce); // Associated data is the 32-bit packet length. const packetLength = data.length; this.associatedData[0] = packetLength >>> 24; this.associatedData[1] = packetLength >>> 16; this.associatedData[2] = packetLength >>> 8; this.associatedData[3] = packetLength; cipher.setAAD(this.associatedData); if (!this.isEncryption) { if (!this.tag) { throw new Error('AES-GCM tag was not set before decrypting.'); } cipher.setAuthTag(this.tag); } const result = cipher.update(data); if (result.length !== data.length) { const message = 'Result from encrypt/decrypt has invalid length ' + `${result.length}, expected ${data.length}.`; throw new Error(message); } cipher.final(); if (this.isEncryption) { this.tag = cipher.getAuthTag(); } else { this.tag = null; } // Increment the counter (last 8 bytes of the nonce) as a big-endian integer. // First increment the last byte, and if it reaches 0 then increment the // next-to-last byte, and so on. let k = 12; while (--k >= 4) { this.nonce[k]++; if (this.nonce[k] !== 0) { break; } } return Promise.resolve(result); } async sign(data) { if (!this.tag) { throw new Error('AES-GCM tag was not obtained by encrypting.'); } return this.tag; } async verify(data, signature) { if (signature.length !== this.digestLength) { throw new Error('Incorrect AES-GCM tag length.'); } this.tag = signature; return true; } dispose() { } } //# sourceMappingURL=nodeEncryption.js.map