/* eslint-disable complexity */ /* eslint-disable lodash/prefer-lodash-typecheck */ /** * @fileOverview Support for encryption and decryption of account details */ 'use strict'; const _ = require('lodash'); const utils = require(global.pathPrefix + 'utils.js'); module.exports = { decryptCard, decryptWorldpayMerchant, encryptCard, encryptCardMaintainingAccount, decryptCardMaintainingAccount }; /** * Decrypts a string * * @param {string} encrypted - the encrypted string * @returns {?String} - decrypted string, or null if nothing to decrypt * @throws {Object} - throws an exception on decryption failure */ function decryptIfExistsV1(encrypted) { // // Check if there is anything to decrypt // if (_.isUndefined(encrypted) || encrypted === '') { return null; } const decrypted = utils.decryptDataV1(encrypted); if (_.isString(decrypted)) { return decrypted; } else { throw decrypted; // Throw an exception for any errors. } } /** * Decrypts a string * * @param {string} encrypted - the encrypted string * @param {string} key - the key to decrypt with * @param {string} userID - the ID of the user * @returns {?String} - decrypted string, or null if nothing to decrypt * @throws {Object} - throws an exception on decryption failure */ function decryptIfExistsV3(encrypted, key, userID) { // // Check if there is anything to decrypt // if (_.isUndefined(encrypted) || encrypted === '') { return null; } return utils.decryptDataV3(encrypted, key, userID); } /** * Encrypts a string * * @param {string} plainString - the encrypted string * @param {string} key - the key to encrypt with * @param {string} userID - the ID of the user * @returns {?String} - encrypted string, or null if nothing to encrypt * @throws {Object} - throws an exception on encryption failure */ function encryptIfExistsV3(plainString, key, userID) { // // Check if there is anything to encrypt // if (_.isUndefined(plainString) || plainString === '') { return null; } return utils.encryptDataV3(plainString, key, userID); } /** * This function encrypts the various card details as required and available. * * @param {Object} account - the account containing the details to encrypt * @param {string} key - the key from the device * @param {string} userID - the _id of the user as a string * * @returns {?Object} - an object with the decrypted details * @throws {TypeError} - an error */ function encryptCard(account, key, userID) { /** * Encrypt and store the card details. */ let temp; const encryptedCardDetails = {}; /** * CardPAN */ temp = encryptIfExistsV3( account.CardPanToBeEncrypted, key, userID); if (!_.isString(temp)) { throw new TypeError('Error when encrypting CardPAN.'); } encryptedCardDetails.CardPANEncrypted = temp; /** * CardExpiry */ temp = encryptIfExistsV3( account.CardExpiryToBeEncrypted, key, userID); if (!_.isString(temp)) { throw new TypeError('Error when encrypting CardExpiry.'); } encryptedCardDetails.CardExpiryEncrypted = temp; /** * CardValidFrom */ if (account.CardValidFromToBeEncrypted && account.CardValidFromToBeEncrypted !== '') { temp = utils.encryptDataV3( account.CardValidFromToBeEncrypted, key, userID); if (!_.isString(temp)) { throw new TypeError('Error when encrypting CardValidFrom.'); } encryptedCardDetails.CardValidFromEncrypted = temp; } /** * IssueNumber */ if (account.IssueNumberToBeEncrypted) { temp = utils.encryptDataV3( account.IssueNumberToBeEncrypted.toString(), key, userID); if (!_.isString(temp)) { throw new TypeError('Error when encrypting IssueNumber.'); } encryptedCardDetails.IssueNumberEncrypted = temp; } return encryptedCardDetails; } /** * This function calls encrypt card, deletes unencrypted details and adds the encrypted details to the account object that was provided * * @param {Object} data - the data contaning an account which contains the details to encrypt * @param {string} key - the key from the device * @param {string} userID - the _id of the user as a string * * @returns {?Object} - an object with the decrypted details * @throws {TypeError} - an error */ function encryptCardMaintainingAccount(data, key, userID) { const clonedAccount = _.cloneDeep(data.Account); const cardInfo = clonedAccount.CreditDebitCardInfo; const encryptedDetails = encryptCard(cardInfo, key, userID); const removeArray = ['CardPanToBeEncrypted', 'CardExpiryToBeEncrypted', 'IssueNumberToBeEncrypted', 'CardValidFromToBeEncrypted']; const temp = _.omit(cardInfo, removeArray); const encryptedCardInfo = _.defaults( {}, encryptedDetails, temp ); clonedAccount.CreditDebitCardInfo = encryptedCardInfo; data.Account = clonedAccount; return data; } /** * This function calls decrypt card, deletes unencrypted details and adds the encrypted details to the account object that was provided * * @param {Object} account - the account containing the details to decrypt * @param {string} key - the key from the device * @param {string} userID - the _id of the user as a string * * @returns {?Object} - an object with the decrypted details or null * @throws {TypeError} - an error */ function decryptCardMaintainingAccount(account, key, userID) { const clonedAccount = _.cloneDeep(account); const cardInfo = clonedAccount.CreditDebitCardInfo; const decryptedDetails = decryptCard(cardInfo, key, userID); if (decryptedDetails) { const removeArray = ['CardExpiryEncrypted', 'CardPANEncrypted', 'IssueNumberEncrypted', 'CardValidFromEncrypted']; const temp = _.omit(cardInfo, removeArray); const decryptedCardInfo = _.defaults( {}, decryptedDetails, temp ); clonedAccount.CreditDebitCardInfo = decryptedCardInfo; return clonedAccount; } else { return null; // decryption failed } } /** * This function decrypts the various card details as required and available. * * @param {Object} account - the account containing the details to decrypt * @param {string} key - the key from the device * @param {string} userID - the _id of the user as a string * * @returns {?Object} - an object with the decrypted details or null * @throws {TypeError} - an error */ function decryptCard(account, key, userID) { const result = {}; let dec = null; // // Decrypt required fields. // If a string is returned than the string is saved to the result // If a specifc error is returned than null is returned (this helps higher level functions throw a specific error) // If null or any other error is returned than an error is thrown // dec = decryptIfExistsV3(account.CardExpiryEncrypted, key, userID); if (_.isString(dec)) { result.expiryMonth = dec.substr(0, 2); result.expiryYear = '20' + dec.substr(3, 2); } else if (dec && typeof _.isObject(dec) && dec.code === 9) { return null; } else { throw new TypeError('Decryption Error'); } dec = decryptIfExistsV3(account.CardPANEncrypted, key, userID); if (_.isString(dec)) { result.cardNumber = dec; } else if (dec && typeof _.isObject(dec) && dec.code === 9) { return null; } else { throw new TypeError('Decryption Error'); } // // Decrypt optional fields // dec = decryptIfExistsV3(account.IssueNumberEncrypted, key, userID); if (_.isString(dec)) { result.IssueNumber = parseInt(dec, 10); } else if (dec && typeof _.isObject(dec) && dec.code === 9) { return null; } else if (dec !== null) { throw new TypeError('Decryption Error'); } dec = decryptIfExistsV3(account.CardValidFromEncrypted, key, userID); if (_.isString(dec)) { result.startMonth = dec.substr(0, 2); result.startYear = '20' + dec.substr(3, 2); } else if (dec && typeof _.isObject(dec) && dec.code === 9) { return null; } else if (dec !== null) { throw new TypeError('Decryption Error'); } return result; } /** * Decrypts the worldpay merchant info, if exists * * @param {Object} account - The account to get the data from * * @returns {?Object} - an object with the decrypted details or null on error */ function decryptWorldpayMerchant(account) { const result = {}; try { const dec = decryptIfExistsV1(account.AcquirerCipher); if (_.isString(dec)) { result.worldpayServiceKey = dec; } else { throw new TypeError('AcquirerCipher missing'); } } catch (error) { // Decryption failed or fields are missing return null; } return result; }