/home/ivoiecob/email.hirewise-va.com/modules/OpenPgpFilesWebclient/js/OpenPgpFileProcessor.js
'use strict';
let
_ = require('underscore'),
ModulesManager = require('%PathToCoreWebclientModule%/js/ModulesManager.js'),
Ajax = require('%PathToCoreWebclientModule%/js/Ajax.js'),
Api = require('%PathToCoreWebclientModule%/js/Api.js'),
Screens = require('%PathToCoreWebclientModule%/js/Screens.js'),
TextUtils = require('%PathToCoreWebclientModule%/js/utils/Text.js'),
Utils = require('%PathToCoreWebclientModule%/js/utils/Common.js'),
ErrorsUtils = require('modules/%ModuleName%/js/utils/Errors.js'),
JscryptoKey = require('modules/CoreParanoidEncryptionWebclientPlugin/js/JscryptoKey.js'),
HexUtils = require('modules/CoreParanoidEncryptionWebclientPlugin/js/utils/Hex.js'),
OpenPgpEncryptor = ModulesManager.run('OpenPgpWebclient', 'getOpenPgpEncryptor')
;
let OpenPgpFileProcessor = {};
OpenPgpFileProcessor.processFileEncryption = async function (oFile, oFilesView, sRecipientEmail,
contactUUID, bIsPasswordMode, bSign, iLifetimeHrs = 0)
{
const
sPath = oFilesView.currentPath(),
sStorageType = oFilesView.storageType(),
oResultData = { result: false },
extendedProps = oFile && oFile.oExtendedProps,
encryptedParanoidKey = extendedProps &&
(oFile.sharedWithMe() ? extendedProps.ParanoidKeyShared : extendedProps.ParanoidKey)
;
if (encryptedParanoidKey)
{
let sPassphrase = '';
//decrypt key
let oPGPDecryptionResult = await OpenPgpEncryptor.decryptData(
encryptedParanoidKey,
sPassphrase
);
if (oPGPDecryptionResult.passphrase)
{
// saving passphrase so that it won't be asked again until encrypt popup is closed
sPassphrase = oPGPDecryptionResult.passphrase;
oResultData.passphrase = sPassphrase;
}
if (oPGPDecryptionResult.result)
{
const sKey = oPGPDecryptionResult.result;
//encrypt Paranoid key
const aPublicKeys = sRecipientEmail && !bIsPasswordMode ?
await OpenPgpEncryptor.getPublicKeysByContactsAndEmails([contactUUID], [sRecipientEmail])
: [];
const oPrivateKey = bSign ? await OpenPgpEncryptor.getCurrentUserPrivateKey() : null;
const oPGPEncryptionResult = await OpenPgpEncryptor.encryptData(
sKey,
aPublicKeys,
[oPrivateKey],
bIsPasswordMode,
bSign,
sPassphrase
);
if (
!oPGPEncryptionResult.result
|| oPGPEncryptionResult.hasErrors()
|| oPGPEncryptionResult.hasNotices()
)
{
ErrorsUtils.showPgpErrorByCode(
oPGPEncryptionResult,
Enums.PgpAction.Encrypt
);
}
else
{
const { data: sEncryptedKey, password: sPassword } = oPGPEncryptionResult.result;
if (sEncryptedKey)
{
//Update ParanoidKeyPublic
const bUpdateExtendedProps = await this.updateFileExtendedProps(
oFile,
{ ParanoidKeyPublic: sEncryptedKey }
);
if (bUpdateExtendedProps)
{
//creating a public link
let oPublicLinkResult = await this.createPublicLink(
oFile.storageType(),
oFile.path(),
oFile.fileName(),
oFile.size(),
false,
sRecipientEmail,
bIsPasswordMode ? Enums.EncryptionBasedOn.Password : Enums.EncryptionBasedOn.Key,
iLifetimeHrs
);
if (oPublicLinkResult.result)
{
oFilesView.refresh();
oResultData.result = true;
oResultData.password = sPassword;
oResultData.link = oPublicLinkResult.link;
} else {
Screens.showError(oPublicLinkResult.errorMessage || TextUtils.i18n('%MODULENAME%/ERROR_CREATE_PUBLIC_LINK'));
}
}
else
{
Screens.showError(TextUtils.i18n('%MODULENAME%/ERROR_UPDATING_KEY'));
}
}
}
}
else
{
ErrorsUtils.showPgpErrorByCode(oPGPDecryptionResult, Enums.PgpAction.DecryptVerify);
}
}
else
{
Screens.showError('%MODULENAME%/ERROR_READING_KEY');
}
return oResultData;
};
OpenPgpFileProcessor.decryptFile = async function (oBlob, sRecipientEmail, sPassword, bPasswordBasedEncryption, sParanoidKeyPublic, sInitializationVector)
{
let oResult = {
result: false
};
try
{
//key decryption
let oPGPDecryptionResult = await OpenPgpEncryptor.decryptData(
sParanoidKeyPublic,
sPassword,
bPasswordBasedEncryption
);
if (!oPGPDecryptionResult.result)
{
ErrorsUtils.showPgpErrorByCode(oPGPDecryptionResult, Enums.PgpAction.DecryptVerify);
}
else
{
//signature verification
if (oPGPDecryptionResult.validKeyNames
&& oPGPDecryptionResult.validKeyNames.length
)
{
const oCurrentUserPrivateKey = await OpenPgpEncryptor.getCurrentUserPrivateKey();
if (!oCurrentUserPrivateKey
|| !oPGPDecryptionResult.validKeyNames.includes(oCurrentUserPrivateKey.getUser())
)
{//Paranoid-key was signed with a foreign key
const sReport = TextUtils.i18n('%MODULENAME%/REPORT_SUCCESSFULL_SIGNATURE_VERIFICATION')
+ oPGPDecryptionResult.validKeyNames.join(', ').replace(/</g, "<").replace(/>/g, ">");
Screens.showReport(sReport)
}
}
else if (oPGPDecryptionResult.notices && _.indexOf(oPGPDecryptionResult.notices, Enums.OpenPgpErrors.VerifyErrorNotice) !== -1)
{
Screens.showError(TextUtils.i18n('%MODULENAME%/ERROR_SIGNATURE_NOT_VERIFIED'));
}
//file decryption
const sKey = oPGPDecryptionResult.result;
let oDecryptedFileData = await this.decryptAsSingleChunk(oBlob, sKey, sInitializationVector);
//save decrypted file
if (oDecryptedFileData)
{
let oResBlob = new Blob(
[ oDecryptedFileData ],
{
type: "octet/stream",
lastModified: new Date()
}
);
oResult.result = true;
oResult.blob = oResBlob;
}
}
return oResult;
}
catch (oError)
{
return oResult;
}
};
OpenPgpFileProcessor.createPublicLink = async function (sType, sPath, sFileName, iSize, bEncryptLink = false,
sRecipientEmail = '', sPgpEncryptionMode = '', iLifetimeHrs = 0, bIsFolder = false)
{
let sLink = '';
let oResult = {result: false};
const sPassword = bEncryptLink || sPgpEncryptionMode === Enums.EncryptionBasedOn.Password
? OpenPgpEncryptor.generatePassword()
: '';
const oPromiseCreatePublicLink = new Promise( (resolve, reject) => {
const fResponseCallback = (response, request) => {
if (response.Result && response.Result.link) {
resolve(response.Result);
}
var errorText = Api.getErrorByCode(response, TextUtils.i18n('%MODULENAME%/ERROR_PUBLIC_LINK_CREATION'));
reject(new Error(errorText));
};
let oParams = {
'Type': sType,
'Path': sPath,
'Name': sFileName,
'Size': iSize,
'IsFolder': bIsFolder,
'RecipientEmail': sRecipientEmail,
'PgpEncryptionMode': sPgpEncryptionMode,
'LifetimeHrs': iLifetimeHrs
};
if (bEncryptLink)
{
oParams.Password = sPassword;
}
Ajax.send(
'OpenPgpFilesWebclient',
'CreatePublicLink',
oParams,
fResponseCallback,
this
);
});
try
{
const oLinkData = await oPromiseCreatePublicLink;
oResult.result = true;
oResult.link = oLinkData?.link || '';
oResult.password = oLinkData?.password || sPassword;
}
catch (oError)
{
if (oError && oError.message)
{
oResult.errorMessage = oError.message;
}
}
return oResult;
};
OpenPgpFileProcessor.getFileContentByUrl = async function (sDownloadUrl, onDownloadProgressCallback)
{
let response = await fetch(sDownloadUrl);
if (response.ok)
{
const reader = response.body.getReader();
let iReceivedLength = 0;
let aChunks = [];
while (true)
{
const {done, value} = await reader.read();
if (done)
{
break;
}
iReceivedLength += value.length;
aChunks.push(value);
if (_.isFunction(onDownloadProgressCallback))
{
onDownloadProgressCallback(iReceivedLength);
}
}
return new Blob(aChunks);
}
else
{
return false;
}
};
OpenPgpFileProcessor.saveBlob = async function (oBlob, sFileName)
{
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(oBlob, sFileName);
return;
}
let blobUrl = window.URL.createObjectURL(oBlob);
let link = document.createElement("a");
link.href = blobUrl;
link.download = sFileName;
document.body.appendChild(link);
link.click();
window.URL.revokeObjectURL(blobUrl);
};
OpenPgpFileProcessor.processFileDecryption = async function (sFileName, sDownloadUrl, sRecipientEmail, sPassword, sEncryptionMode, sParanoidKeyPublic, sInitializationVector)
{
let oBlob = await this.getFileContentByUrl(sDownloadUrl);
if (oBlob instanceof Blob)
{
let oDecryptionResult = await this.decryptFile(
oBlob,
sRecipientEmail,
sPassword,
sEncryptionMode === Enums.EncryptionBasedOn.Password,
sParanoidKeyPublic,
sInitializationVector
);
if (oDecryptionResult.result)
{
this.saveBlob(
oDecryptionResult.blob,
sFileName
);
}
else
{
// Error with details is already shown in decryptFile method
// Screens.showError(TextUtils.i18n('%MODULENAME%/ERROR_ON_DOWNLOAD'));
}
}
else
{
Screens.showError(TextUtils.i18n('%MODULENAME%/ERROR_ON_DOWNLOAD'));
}
};
OpenPgpFileProcessor.getFileNameForDownload = function (sFileName, sRecipientEmail)
{
const sFileNameWithoutExtension = Utils.getFileNameWithoutExtension(sFileName);
const sDelimiter = '_' + sRecipientEmail;
const aNameParts = sFileNameWithoutExtension.split(sDelimiter);
let sNewName = '';
if (aNameParts.length <= 2)
{
sNewName = aNameParts.join('');
}
else
{
//If the files name contains more than one entry of a recipient email, only the last entry is removed
for (let i = 0; i < aNameParts.length; i++)
{
sNewName += aNameParts[i];
sNewName += i < (aNameParts.length - 2) ? sDelimiter : '';
}
}
return sNewName;
};
OpenPgpFileProcessor.updateFileExtendedProps = async function (oFile, oExtendedProps) {
//Update file extended props
let oPromiseUpdateExtendedProps = new Promise( (resolve, reject) => {
Ajax.send(
'Files',
'UpdateExtendedProps',
{
Type: oFile.storageType(),
Path: oFile.path(),
Name: oFile.fileName(),
ExtendedProps: oExtendedProps
},
oResponse => {
if (oResponse.Result === true)
{
resolve(true);
}
resolve(false);
},
this
);
});
return await oPromiseUpdateExtendedProps;
};
OpenPgpFileProcessor.decryptAsSingleChunk = async function (oBlob, sKey, sInitializationVector) {
let oKey = await JscryptoKey.getKeyFromString(sKey);
let aEncryptedData = await new Response(oBlob).arrayBuffer();
let oDecryptedArrayBuffer = await crypto.subtle.decrypt(
{
name: 'AES-CBC',
iv: new Uint8Array(HexUtils.HexString2Array(sInitializationVector))
},
oKey,
aEncryptedData
);
return new Uint8Array(oDecryptedArrayBuffer);
};
module.exports = OpenPgpFileProcessor;