"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const teradatasql_1 = require("teradatasql");
const fs = __importStar(require("fs"));
const crypto = __importStar(require("crypto"));
function convertJavaNames(sName, nKeySizeInBits = 0, sMode = "") {
    if (sName === "AES") {
        sName = "aes-" + nKeySizeInBits.toString() + "-" + sMode.toLowerCase();
    }
    return sName;
}
function createPasswordEncryptionKeyFile(sTransformation, sAlgorithm, sMatch, sMac, nKeySizeInBits, sPassKeyFileName) {
    const nKeySizeInBytes = nKeySizeInBits / 8;
    const abyKey = crypto.randomBytes(nKeySizeInBytes);
    const nMacBlockSizeBytes = 64;
    const abyMacKey = crypto.randomBytes(nMacBlockSizeBytes);
    const sKeyHexDigits = abyKey.toString("hex");
    const sMacKeyHexDigits = abyMacKey.toString("hex");
    const fileData = "# Teradata SQL Driver password encryption key file\n" +
        "version=1\n" +
        "transformation=" +
        sTransformation +
        "\n" +
        "algorithm=" +
        sAlgorithm +
        "\n" +
        "match=" +
        sMatch +
        "\n" +
        "key=" +
        sKeyHexDigits +
        "\n" +
        "mac=" +
        sMac +
        "\n" +
        "mackey=" +
        sMacKeyHexDigits +
        "\n";
    const asTransformationParts = sTransformation.split("/");
    if (asTransformationParts.length !== 3) {
        console.log(">>> Invalid transformation: " + sTransformation);
        process.exit(1);
    }
    fs.writeFileSync(sPassKeyFileName, fileData, { encoding: "latin1" });
    return [abyKey, abyMacKey];
}
function createEncryptedPasswordFile(sTransformation, sAlgorithm, sMatch, sMac, sMode, nKeySizeInBits, abyKey, abyMacKey, sEncPassFileName, nCipherBlockSizeInBytes, sPassword) {
    let abyPassword = Buffer.from(sPassword, "utf8");
    const abyIV = crypto.randomBytes(nCipherBlockSizeInBytes);
    const octetStringTag = 0x04;
    let abyASN1EncodedIV = Buffer.allocUnsafe(2);
    abyASN1EncodedIV[0] = octetStringTag;
    abyASN1EncodedIV[1] = abyIV.length;
    abyASN1EncodedIV = Buffer.concat([abyASN1EncodedIV, abyIV], abyASN1EncodedIV.length + abyIV.length);
    const nPlaintextByteCount = (Math.floor(abyPassword.length / 512) + 1) * 512;
    const nTrailerByteCount = nPlaintextByteCount - abyPassword.length;
    const emptyBuffer = Buffer.alloc(nTrailerByteCount);
    abyPassword = Buffer.concat([abyPassword, emptyBuffer], abyPassword.length + emptyBuffer.length);
    const nodeAlgorithmName = convertJavaNames(sAlgorithm, nKeySizeInBits, sMode);
    const cipher = crypto.createCipheriv(nodeAlgorithmName, abyKey, abyIV);
    let abyEncryptedPassword = cipher.update(abyPassword);
    const finalCipher = cipher.final();
    abyEncryptedPassword = Buffer.concat([abyEncryptedPassword, finalCipher], abyEncryptedPassword.length + finalCipher.length);
    const sEncryptedPasswordHexDigits = abyEncryptedPassword.toString("hex");
    const sASN1EncodedIVHexDigits = abyASN1EncodedIV.toString("hex");
    const abyTransformation = Buffer.from(sTransformation, "utf8");
    const abyContentLength = abyEncryptedPassword.length + abyTransformation.length + abyASN1EncodedIV.length;
    const abyContent = Buffer.concat([abyEncryptedPassword, abyTransformation, abyASN1EncodedIV], abyContentLength);
    sMac = sMac.slice(4).toLowerCase();
    const hmac = crypto.createHmac(sMac, abyMacKey);
    hmac.update(abyContent);
    const sHashHexDigits = hmac.digest("hex");
    const fileData = "# Teradata SQL Driver encrypted password file\n" +
        "version=1\n" +
        "match=" +
        sMatch +
        "\n" +
        "password=" +
        sEncryptedPasswordHexDigits +
        "\n" +
        "params=" +
        sASN1EncodedIVHexDigits +
        "\n" +
        "hash=" +
        sHashHexDigits +
        "\n";
    fs.writeFileSync(sEncPassFileName, fileData, { encoding: "latin1" });
}
function loadPropertiesFile(sFileName) {
    const properties = {};
    const content = fs.readFileSync(sFileName, { encoding: "latin1" });
    const lines = content.split("\n");
    lines.forEach((line) => {
        line = line.trim();
        if (line.indexOf("#") !== 0) {
            const asTokens = line.split("=", 2);
            if (asTokens.length === 2) {
                const sKey = asTokens[0];
                const sValue = asTokens[1];
                properties[sKey] = sValue;
            }
        }
    });
    return properties;
}
function decryptPassword(sPassKeyFileName, sEncPassFileName) {
    const mapPassKey = loadPropertiesFile(sPassKeyFileName);
    const mapEncPass = loadPropertiesFile(sEncPassFileName);
    const algorithmString = "algorithm";
    const hashString = "hash";
    const keyString = "key";
    const macString = "mac";
    const mackeyString = "mackey";
    const matchString = "match";
    const paramsString = "params";
    const passwordString = "password";
    const transformationString = "transformation";
    const versionString = "version";
    if (mapPassKey[versionString] !== "1") {
        console.log("Unrecognized version %s in file %s", mapPassKey[versionString], sPassKeyFileName);
        process.exit(1);
    }
    if (mapEncPass[versionString] !== "1") {
        console.log("Unrecognized version %s in file %s", mapPassKey[versionString], sEncPassFileName);
        process.exit(1);
    }
    if (mapPassKey[matchString] !== mapEncPass[matchString]) {
        console.log("Match value differs between files %s and %s", sPassKeyFileName, sEncPassFileName);
        process.exit(1);
    }
    const sTransformation = mapPassKey[transformationString];
    const sAlgorithm = mapPassKey[algorithmString];
    const sKeyHexDigits = mapPassKey[keyString];
    const sMACAlgorithm = mapPassKey[macString];
    const sMacKeyHexDigits = mapPassKey[mackeyString];
    const abyTransformation = Buffer.from(sTransformation, "utf8");
    const abyKey = Buffer.from(sKeyHexDigits, "hex");
    const abyMacKey = Buffer.from(sMacKeyHexDigits, "hex");
    const abyEncryptedPassword = Buffer.from(mapEncPass[passwordString], "hex");
    const abyASN1EncodedIV = Buffer.from(mapEncPass[paramsString], "hex");
    const asTransformationParts = sTransformation.split("/");
    const sMode = asTransformationParts[1];
    if (sAlgorithm !== asTransformationParts[0]) {
        console.log("Algorithm differs from transformation in file %s", sPassKeyFileName);
        process.exit(1);
    }
    const abyContentLength = abyEncryptedPassword.length + abyTransformation.length + abyASN1EncodedIV.length;
    const abyContent = Buffer.concat([abyEncryptedPassword, abyTransformation, abyASN1EncodedIV], abyContentLength);
    const sMac = sMACAlgorithm.slice(4).toLowerCase();
    const hmac = crypto.createHmac(sMac, abyMacKey);
    hmac.update(abyContent);
    const hashHexDigits = hmac.digest("hex");
    const sHashHexDigits = mapEncPass[hashString];
    if (hashHexDigits !== sHashHexDigits) {
        console.log("Hash mismatch indicates possible tampering with file %s or %s", sPassKeyFileName, sEncPassFileName);
        process.exit(1);
    }
    const nKeySizeInBytes = abyKey.length;
    const nKeySizeInBits = nKeySizeInBytes * 8;
    const nodeAlgorithmName = convertJavaNames(sAlgorithm, nKeySizeInBits, sMode);
    const abyIV = abyASN1EncodedIV.slice(2, 2 + abyASN1EncodedIV[1]);
    const decipher = crypto.createDecipheriv(nodeAlgorithmName, abyKey, abyIV);
    const decrypted = decipher.update(abyEncryptedPassword);
    const finalCipher = decipher.final();
    const abyPassword = Buffer.concat([decrypted, finalCipher], decrypted.length + finalCipher.length);
    const sPassword = abyPassword.slice(0, abyPassword.indexOf("\x00")).toString("utf8");
    console.log("Decrypted password: %s", sPassword);
}
if (process.argv.length !== 10) {
    console.log("Parameters: Transformation KeySizeInBits MAC PasswordEncryptionKeyFileName EncryptedPasswordFileName" + " Hostname Username Password");
    process.exit(1);
}
const sTransformation = process.argv[2];
const sKeySizeInBits = process.argv[3];
const sMac = process.argv[4];
const sPassKeyFileName = process.argv[5];
const sEncPassFileName = process.argv[6];
const sHostname = process.argv[7];
const sUsername = process.argv[8];
let sPassword = process.argv[9];
const asTransformationParts = sTransformation.split("/");
if (asTransformationParts.length !== 3) {
    console.log("Invalid transformation: " + sTransformation);
    process.exit(1);
}
const sAlgorithm = asTransformationParts[0];
const sMode = asTransformationParts[1];
const sPadding = asTransformationParts[2];
if ("AES".indexOf(sAlgorithm) < 0) {
    console.log("Unknown algorithm " + sAlgorithm);
    process.exit(1);
}
if (["CBC", "CFB", "OFB"].indexOf(sMode) < 0) {
    console.log("Unknown mode " + sMode);
    process.exit(1);
}
if (["PKCS5Padding", "NoPadding"].indexOf(sPadding) < 0) {
    console.log("Unknown padding " + sPadding);
    process.exit(1);
}
if (["HmacSHA1", "HmacSHA256"].indexOf(sMac) < 0) {
    console.log("Unknown MAC algorithm " + sMac);
    process.exit(1);
}
if (!sPassword) {
    console.log("Password cannot be zero length");
    process.exit(1);
}
const nKeySizeInBits = parseInt(sKeySizeInBits, 10);
const match = Date.now().toString();
let nCipherBlockSizeInBytes = 0;
if (sAlgorithm === "AES") {
    nCipherBlockSizeInBytes = 16;
}
else {
    nCipherBlockSizeInBytes = 8;
}
const encryptKeys = createPasswordEncryptionKeyFile(sTransformation, sAlgorithm, match, sMac, nKeySizeInBits, sPassKeyFileName);
createEncryptedPasswordFile(sTransformation, sAlgorithm, match, sMac, sMode, nKeySizeInBits, encryptKeys[0], encryptKeys[1], sEncPassFileName, nCipherBlockSizeInBytes, sPassword);
decryptPassword(sPassKeyFileName, sEncPassFileName);
sPassword = "ENCRYPTED_PASSWORD(file:" + sPassKeyFileName + ",file:" + sEncPassFileName + ")";
const connParams = {
    host: sHostname,
    password: sPassword,
    user: sUsername,
};
try {
    const con = new teradatasql_1.TeradataConnection();
    con.connect(connParams);
    const cur = con.cursor();
    cur.execute("select user, session");
    const row = cur.fetchone();
    console.log(row);
    cur.close();
    con.close();
}
catch (error) {
    if (error instanceof teradatasql_1.OperationalError) {
        console.log(error.message);
    }
    else {
        console.log(error);
    }
}
//# sourceMappingURL=TJEncryptPassword.js.map