const operations = crypto.subtle const pbkdf2_iterations = 250000 function stringToArrayBuffer(str) { var buf = new ArrayBuffer(str.length); var bufView = new Uint8Array(buf); for (var i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } return buf; } function arrayBufferToString(str) { var byteArray = new Uint8Array(str); var byteString = ''; for (var i = 0; i < byteArray.byteLength; i++) { byteString += String.fromCodePoint(byteArray[i]); } return byteString; } function formatDate (date) { const d = new Date(date) const hours = d.getHours().toString().padStart(2, '0') const minutes = d.getMinutes().toString().padStart(2, '0') const formattedTime = `${hours}:${minutes}` return `${d.toLocaleDateString()} ${formattedTime}` } const EncryptionPlugin = { install(Vue, options) { Vue.prototype.deriveKeyFromPassphrase = async (passphrase, salt) => { const encoder = new TextEncoder(); const keyFromPassword = await operations.importKey( "raw", encoder.encode(passphrase), "PBKDF2", false, ["deriveKey"] ) return await operations.deriveKey( { name: "PBKDF2", salt: stringToArrayBuffer(salt), iterations: pbkdf2_iterations, hash: "SHA-256", }, keyFromPassword, { name: "AES-GCM", length: 256 }, true, ["wrapKey", "unwrapKey"] ) }, Vue.prototype.generateKeyPair = async () => { return await operations.generateKey( { name: "RSA-OAEP", modulusLength: 4096, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" }, true, ["encrypt", "decrypt"] ); }, Vue.prototype.wrapKey = async (key, wrappingKey, iv) => { return btoa(arrayBufferToString(await operations.wrapKey( "jwk", key, wrappingKey, {name: "AES-GCM", iv: stringToArrayBuffer(iv)} ))) }, Vue.prototype.unwrapKey = async (unwrappingKey, armored_jwk_data, iv, args) => { return await operations.unwrapKey( "jwk", stringToArrayBuffer(atob(armored_jwk_data)), unwrappingKey, {name: "AES-GCM", iv: stringToArrayBuffer(iv)}, { name: "RSA-OAEP", hash: "SHA-256", }, true, args, ) }, Vue.prototype.encrypt = async function(data) { return btoa(arrayBufferToString(await operations.encrypt( { name: "RSA-OAEP" }, this.$store.state.encryption.keyPair.publicKey, stringToArrayBuffer(data), ))) }, Vue.prototype.decrypt = async function(armored_data) { return arrayBufferToString(await operations.decrypt( { name: "RSA-OAEP" }, this.$store.state.encryption.keyPair.privateKey, stringToArrayBuffer(atob(armored_data)) )) }, Vue.prototype.decryptObject = async function(efields, obj) { // Decrypt all fields and return a new object var newobj = {} await Promise.all(Object.keys(obj).map(async field => { if (efields.includes(field) && obj[field] != null && obj[field] != "") { // TODO: Catch error newobj[field] = await this.decrypt(obj[field]) } else { newobj[field] = obj[field] } })) return newobj }, Vue.prototype.encryptObject = async function(efields, obj) { // Encrypt all fields and return a new object var newobj = {} await Promise.all(Object.keys(obj).map(async field => { if (efields.includes(field) && obj[field] != null) { // TODO: Catch error newobj[field] = await this.encrypt(obj[field]) } else { newobj[field] = obj[field] } })) return newobj }, Vue.prototype.object_edit = async function (url_edit, url_create, type, method, obj) { let url = null if (obj.id == undefined || obj.id == null) { url = Urls[url_create]() } else { url = Urls[url_edit](obj.id) } const efields = this.$store.getters[`${type}/encryptedFields`] try { const newobj = await this.encryptObject(efields, obj) const response = await this.$http[method](url, newobj) if (method != "delete") { return await this.decryptObject(efields, response.data.object) } } catch (err) { let msg = "{{_('Error during edition') | escapejs}}" if (method == "delete") { msg = "{{_('Error during deletion') | escapejs}}" } Swal.fire({title: msg, icon: "error", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) throw err } } } }