UNPKG

oracledb

Version:

A Node.js module for Oracle Database access from JavaScript and TypeScript

198 lines (177 loc) 7.6 kB
// Copyright (c) 2022, 2023, Oracle and/or its affiliates. //----------------------------------------------------------------------------- // // This software is dual-licensed to you under the Universal Permissive License // (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License // 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose // either license. // // If you elect to accept the software under the Apache License, Version 2.0, // the following applies: // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //----------------------------------------------------------------------------- 'use strict'; const { Buffer } = require('buffer'); const crypto = require('crypto'); let algorithm = 'aes-256-cbc'; const _appendBuffer = Buffer.from([0x00, 0x01]); /** * A single Instance which handles all Encrypt, Decrypt, * hash related to password verifiers. */ class EncryptDecrypt { // Key length is dependent on the algorithm. In this case for aes192, it is // 24 bytes (192 bits). _decrypt(key, val) { const iv = Buffer.alloc(16, 0); // Initialization vector, same is used in server const decipher = crypto.createDecipheriv(algorithm, key, iv); decipher.setAutoPadding(false); let decrypted = decipher.update(val); decrypted = Buffer.concat([decrypted, decipher.final()]); return decrypted; } _encrypt(key, val, padding) { const block_size = 16; const iv = Buffer.alloc(block_size, 0); const n = block_size - (val.length % block_size); const nv = Buffer.alloc(n, n); if (n > 0) { if (padding) { val += Buffer.alloc(n); } else { val = Buffer.concat([val, nv]); } } const cipher = crypto.createCipheriv(algorithm, key, iv); let encrypted = cipher.update(val); encrypted = Buffer.concat([encrypted, cipher.final()]); if (!padding) { encrypted = encrypted.slice(0, val.length); } val.fill(0); return encrypted; } // Encrypt password and newPassword using comboKey _setEncryptedPasswordBuffers(passwordBytes, newPasswordBytes, comboKey, authObj) { const salt = Buffer.alloc(16); crypto.randomFillSync(salt, 0, 16); const temp = Buffer.concat([salt, passwordBytes]); authObj.encodedPassword = this._encrypt(comboKey, temp); temp.fill(0); authObj.encodedPassword = authObj.encodedPassword.slice().toString('hex').toUpperCase(); if (newPasswordBytes) { const newPasswordWithSalt = Buffer.concat([salt, newPasswordBytes]); authObj.encodedNewPassword = this._encrypt(comboKey, newPasswordWithSalt); newPasswordWithSalt.fill(0); authObj.encodedNewPassword = authObj.encodedNewPassword.slice().toString('hex').toUpperCase(); } // reset Buffers passwordBytes.fill(0); if (newPasswordBytes) { newPasswordBytes.fill(0); } } /** * updates authObject with required data. * * @param {object} sessionData The key/value pairs returned from OSESS key rpc * @param {string} password Current Password of user * @param {string} newPassword New password to be updated * @param {boolean} verifier11G Verifier type 11g or not(12c) */ updateVerifierData(sessionData, password, newPassword, verifier11G, authObj) { let keyLen = 32; let hashAlg = 'sha512'; const verifierData = Buffer.from(sessionData['AUTH_VFR_DATA'], 'hex'); const encodedServerKey = Buffer.from(sessionData['AUTH_SESSKEY'], 'hex'); let iterations = Number(sessionData['AUTH_PBKDF2_VGEN_COUNT']); const passwordBytes = Buffer.from(password, 'utf8'); let passwordHash; let passwordKey; if (verifier11G) { algorithm = 'aes-192-cbc'; keyLen = 24; hashAlg = 'sha1'; const h = crypto.createHash(hashAlg); h.update(passwordBytes); h.update(verifierData); const ph = h.digest(); passwordHash = Buffer.alloc(ph.length + 4); ph.copy(passwordHash, 0, 0, ph.length); } else { algorithm = 'aes-256-cbc'; const temp = Buffer.from('AUTH_PBKDF2_SPEEDY_KEY', 'utf8'); const salt = Buffer.concat([verifierData, temp]); passwordKey = crypto.pbkdf2Sync(passwordBytes, salt, iterations, 64, 'sha512'); const h = crypto.createHash(hashAlg); h.update(passwordKey); h.update(verifierData); passwordHash = h.digest().slice(0, keyLen); } let newPasswordBytes; if (newPassword) { newPasswordBytes = Buffer.from(newPassword, 'utf8'); } const sessionKeyParta = this._decrypt(passwordHash, encodedServerKey); const sessionKeyPartb = Buffer.alloc(sessionKeyParta.length); crypto.randomFillSync(sessionKeyPartb); const encodedClientKey = this._encrypt(passwordHash, sessionKeyPartb); if (sessionKeyParta.length === 48) { authObj.sessionKey = encodedClientKey.slice().toString('hex').toUpperCase().slice(0, 96); const buf = Buffer.alloc(24); for (let i = 16; i <= 40; i++) { buf[i - 16] = sessionKeyParta[i] ^ sessionKeyPartb[i]; } const part1 = crypto.createHash("md5").update(buf.subarray(0, 16)).digest(); const part2 = crypto.createHash("md5").update(buf.subarray(16)).digest(); authObj.comboKey = Buffer.concat([part1, part2]).slice(0, keyLen); } else { authObj.sessionKey = encodedClientKey.slice().toString('hex').toUpperCase().slice(0, 64); const mixingSalt = Buffer.from(sessionData['AUTH_PBKDF2_CSK_SALT'], 'hex'); iterations = Number(sessionData['AUTH_PBKDF2_SDER_COUNT']); const partABKey = Buffer.concat([sessionKeyPartb.slice(0, keyLen), sessionKeyParta.slice(0, keyLen)]); const partABKeyStr = partABKey.toString('hex').toUpperCase(); const partABKeyBuffer = Buffer.from(partABKeyStr, 'utf8'); authObj.comboKey = crypto.pbkdf2Sync(partABKeyBuffer, mixingSalt, iterations, keyLen, 'sha512'); } const salt = Buffer.alloc(16); if (!verifier11G) { crypto.randomFillSync(salt, 0, 16); const temp = Buffer.concat([salt, passwordKey]); authObj.speedyKey = this._encrypt(authObj.comboKey, temp); authObj.speedyKey = authObj.speedyKey.slice(0, 80).toString('hex').toUpperCase(); } this._setEncryptedPasswordBuffers(passwordBytes, newPasswordBytes, authObj.comboKey, authObj); } getEncryptedJSWPData(sessionKey, jdwpData) { let buf = this._encrypt(sessionKey, jdwpData, true); // Add a "01" at the end of the hex encrypted data to indicate the // use of AES encryption buf = buf.slice().toString('hex').toUpperCase(); buf = Buffer.concat([buf, _appendBuffer]); return buf; } updatePasswordsWithComboKey(password, newPassword, comboKey, authObj) { const passwordBytes = Buffer.from(password, 'utf8'); let newPasswordBytes; if (newPassword) { newPasswordBytes = Buffer.from(newPassword, 'utf8'); } this._setEncryptedPasswordBuffers(passwordBytes, newPasswordBytes, comboKey, authObj); } } const encryptDecryptInst = new EncryptDecrypt(); module.exports = encryptDecryptInst;