161 lines
4.6 KiB
JavaScript
161 lines
4.6 KiB
JavaScript
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.keyPair.publicKey,
|
|
stringToArrayBuffer(data),
|
|
)))
|
|
|
|
},
|
|
|
|
Vue.prototype.decrypt = async function(armored_data) {
|
|
|
|
return arrayBufferToString(await operations.decrypt(
|
|
{ name: "RSA-OAEP" },
|
|
this.$store.state.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
|
|
}
|
|
|
|
}
|
|
|
|
}
|