300 lines
9.1 KiB
JavaScript
300 lines
9.1 KiB
JavaScript
/* 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;
|
|
}
|
|
|