This commit is contained in:
2024-09-28 17:37:29 +02:00
parent 8dfafa9404
commit fb4d566007
64 changed files with 32325 additions and 573 deletions
+117
View File
@@ -0,0 +1,117 @@
{% include "vue/plugins.js" %}
Vue.use(VueRouter)
Vue.use(Vuex)
Vue.use(EncryptionPlugin)
Vue.config.delimiters = ["[[", "]]"];
{% for name, path in components.items %}
{% include path %}
Vue.component("{{ name }}", {{ name }})
{% endfor %}
const routes = [
{ path: '/', component: null },
{% for name, path in components.items %}
{
path: {{ name }}.router_path,
name: "{{ name }}",
component: {{ name }},
},
{% endfor %}
]
const encryptionStore = new Vuex.Store({
state: {
aes_key: null,
keyPair: null,
},
mutations: {
update_aes_key (state, key) {
state.aes_key = key
},
update_keyPair (state, keyPair) {
state.keyPair = keyPair
},
}
})
const router = new VueRouter({routes})
const approuter = new Vue({
router,
vuetify: new Vuetify(),
store: encryptionStore,
el: "#main",
data: {
uuid: "{{ user_settings.id }}",
},
computed: {
locked: function() {
return this.$store.state.aes_key == null || this.$store.state.keyPair?.privateKey == null
}
},
mounted: function() {},
methods: {
async load_keys (aes_key) {
const response = await this.$http.get(Urls["users:keys"]())
const iv_private = `${this.uuid}--private`
const iv_public = `${this.uuid}--public`
if (response.data.privateKey != null) {
const keyPair = {
privateKey: await this.unwrapKey(aes_key, response.data.privateKey, iv_private, ["decrypt"]),
publicKey: await this.unwrapKey(aes_key, response.data.publicKey, iv_public, ["encrypt"]),
}
this.$store.commit('update_keyPair', keyPair)
Swal.fire({title: "Successfully loaded K356!", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000});
} else {
const keyPair = await this.generateKeyPair()
await this.$http.post(
Urls["users:keys"](),
{
privateKey: await this.wrapKey(this.keyPair.privateKey, aes_key, iv_private),
publicKey: await this.wrapKey(this.keyPair.publicKey, aes_key, iv_public),
}
)
this.$store.commit('update_keyPair', keyPair)
Swal.fire({title: "Successfully created K356!", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000});
}
},
update_key: function(key) {
this.$store.commit('update_aes_key', key)
this.load_keys(key)
},
lock_me: function() {
this.$store.commit('update_keyPair', null)
this.$store.commit('update_aes_key', null)
}
}
})
router.beforeEach((to, from, next) => {
// Prevent from routing if key is not present.
next(!approuter.locked)
})
+160
View File
@@ -0,0 +1,160 @@
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
}
}
}