bridge-node-server/node_server/utils/encryption.js
Martin Donnelly 57bd6c8e6a init
2018-06-24 21:15:03 +01:00

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;
}