Compare commits

..
4 Commits
8 changed files with 204 additions and 10 deletions
+4
View File
@@ -79,8 +79,12 @@ BOWER_INSTALLED_APPS = [
"https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js", "https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js",
"https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css", "https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css",
"https://fonts.cdnfonts.com/css/jetbrains-mono", "https://fonts.cdnfonts.com/css/jetbrains-mono",
"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css",
"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js",
# "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/python.min.js",
] ]
MIDDLEWARE = [ MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware", "django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.sessions.middleware.SessionMiddleware",
+1 -1
View File
@@ -39,7 +39,7 @@ class BaseQuerySet(models.QuerySet):
"align": "", "align": "",
"encrypted": True, "encrypted": True,
} }
ret[f"{field.name}__name"] = { ret[f"{field.name}__custom_identifier"] = {
"text": f"{field.verbose_name.capitalize()} - Identifier", "text": f"{field.verbose_name.capitalize()} - Identifier",
"align": "", "align": "",
"encrypted": True, "encrypted": True,
@@ -96,7 +96,7 @@
:items="linked_properties" :items="linked_properties"
:items_headers="linked_properties_headers" :items_headers="linked_properties_headers"
:items_relations="{'property': all_properties}" :items_relations="{'property': all_properties}"
:hidden_fields="['name', 'description', 'custom_identifier']" :hidden_fields="['name', 'description', 'custom_identifier', 'item__name', 'item__custom_identifier']"
:non_editable_fields="['item']" :non_editable_fields="['item']"
show_item="property" show_item="property"
group_by="type" group_by="type"
@@ -113,7 +113,7 @@
<ItemRelationList <ItemRelationList
:items="children" :items="children"
:items_headers="children_headers" :items_headers="children_headers"
:hidden_fields="['parent__name']" :hidden_fields="['parent__name', 'parent__custom_identifier']"
:items_relations="{'parent': [object], 'child': all_items}" :items_relations="{'parent': [object], 'child': all_items}"
group_by="type__name" group_by="type__name"
@deleteItem="deleteRelation" @deleteItem="deleteRelation"
@@ -129,7 +129,7 @@
<ItemRelationList <ItemRelationList
:items="parents" :items="parents"
:items_headers="children_headers" :items_headers="children_headers"
:hidden_fields="['child__name']" :hidden_fields="['child__name', 'child__custom_identifier']"
:items_relations="{'child': [object], 'parent': all_items}" :items_relations="{'child': [object], 'parent': all_items}"
group_by="type__name" group_by="type__name"
@deleteItem="deleteRelation" @deleteItem="deleteRelation"
@@ -28,7 +28,7 @@
:items="linked_properties" :items="linked_properties"
:items_headers="linked_properties_headers" :items_headers="linked_properties_headers"
:items_relations="{'item': all_items, 'property': [object]}" :items_relations="{'item': all_items, 'property': [object]}"
:hidden_fields="['name', 'description', 'custom_identifier']" :hidden_fields="['name', 'description', 'custom_identifier', 'property__name', 'property__custom_identifier']"
:non_editable_fields="[]" :non_editable_fields="[]"
show_item="item" show_item="item"
show_url="ItemDetail" show_url="ItemDetail"
@@ -47,7 +47,7 @@
:items="relation_properties" :items="relation_properties"
:items_headers="relation_properties_headers" :items_headers="relation_properties_headers"
:items_relations="{'item': all_items, 'property': [object]}" :items_relations="{'item': all_items, 'property': [object]}"
:hidden_fields="['name', 'description', 'custom_identifier']" :hidden_fields="['name', 'description', 'custom_identifier', 'property__name', 'property__custom_identifier']"
:non_editable_fields="[]" :non_editable_fields="[]"
show_item="relation" show_item="relation"
show_url="ItemRelationDetail" show_url="ItemRelationDetail"
@@ -67,6 +67,9 @@
[[ data.item.name ]] [[ data.item.name ]]
</template> </template>
</template> </template>
<template slot="selection" slot-scope="data">
[[ data.item.custom_identifier ]] - [[ data.item.name ]]
</template>
</v-select> </v-select>
</template> </template>
</template> </template>
+188 -3
View File
@@ -34,6 +34,14 @@
} }
</style> </style>
<!-- highlight.js-->
<link rel="stylesheet" href="{% static "default.min/index.css" %}">
<script src="{% static "highlight.min/index.js" %}"></script>
<script type="text/javascript">
hljs.highlightAll()
</script>
</head> </head>
<body> <body>
<v-app class="font"> <v-app class="font">
@@ -43,15 +51,192 @@
<v-container class="container-xs"> <v-container class="container-xs">
<v-row justify="center"> <v-row justify="center">
<v-dialog v-model="dialog" persistent max-width="290">
<div class="card mt-4 pt-2 ps-lg-2">
<h5 class="card-header">{% trans "K356 Project" %}</h5>
<div class="card-body">
<h2>{% trans "Encryption" %}</h2>
<p>{% trans "Your data are secure in an encrypted way, accessible solely by you. The data are purely encrypted and decrypted in the front-end. The back-end has no access to the encryption key, so there is no way for an intruder to decrypt your data." %}</p>
<div class="accordion" id="enca">
<div class="accordion-item">
<h2 class="accordion-header" id="eh1">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#e1" aria-controls="e1">
{% trans "Encryption" %}
</button>
</h2>
<div id="e1" class="accordion-collapse collapse" aria-labelledby="eh1" data-bs-parent="#enca">
<div class="accordion-body">
{% blocktrans %}
Your data are encrypted using <code>RSA-OAEP</code> algorithm.
{% endblocktrans %}
<pre><code class="language-js">
const operations = crypto.subtle
async encrypt (data) {
return btoa(arrayBufferToString(await operations.encrypt(
{ name: "RSA-OAEP" },
publicKey,
stringToArrayBuffer(data),
)))
}
</code></pre>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="eh2">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#e2" aria-controls="e2">
{% trans "Decryption" %}
</button>
</h2>
<div id="e2" class="accordion-collapse collapse" aria-labelledby="eh2" data-bs-parent="#enca">
<div class="accordion-body">
{% blocktrans %}
Your date are decrypted using the same algorithm, using your own private key.
{% endblocktrans %}
<pre><code class="language-js">
const operations = crypto.subtle
async decrypt (armored_data) {
return arrayBufferToString(await operations.decrypt(
{ name: "RSA-OAEP" },
privateKey,
stringToArrayBuffer(atob(armored_data))
))
}
</code></pre>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="eh3">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#e3" aria-controls="e3">
{% trans "Key wrapping" %}
</button>
</h2>
<div id="e3" class="accordion-collapse collapse" aria-labelledby="eh3" data-bs-parent="#enca">
<div class="accordion-body">
{% blocktrans %}
In order to keep your private/public key pair secret, they are wrapped using a <code>AES-GCM</code> key derived from you password.
{% endblocktrans %}
<pre><code class="language-js">
const operations = crypto.subtle
async wrapKey (key, wrappingKey, iv) => {
return btoa(arrayBufferToString(await operations.wrapKey(
"jwk",
key,
wrappingKey,
{name: "AES-GCM", iv: stringToArrayBuffer(iv)}
)))
}
async unwrapKey (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,
)
}
</code></pre>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="eh4">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#e4" aria-controls="e3">
{% trans "Key derivation" %}
</button>
</h2>
<div id="e4" class="accordion-collapse collapse" aria-labelledby="eh4" data-bs-parent="#enca">
<div class="accordion-body">
{% blocktrans %}
Your public/private key are not stored as-is. They are wrapped using a <code>AES-GCM</code> key, derivated from your password.
A custom salt is used, <i>unique per user</i>, to generate your wrapping key.
{% endblocktrans %}
<pre><code class="language-js">
const operations = crypto.subtle
async deriveKeyFromPassphrase(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"]
)
}
</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card mt-4 pt-2 ps-lg-2">
<h5 class="card-header">{% trans "Login / register" %}</h5>
<div class="card-body d-flex">
<button type="button" class="btn btn-secondary flex-grow-1 flex-shrink-1" @click="dialog = true">{% trans "Click here to login/register" %}</button>
</div>
</div>
</v-row>
<v-row justify="center">
<v-dialog v-model="dialog" max-width="290" class="modal">
<v-card> <v-card>
<v-card-title class="text-h5"> <v-card-title class="text-h5">
{% trans "Login" %} {% trans "Login / Register" %}
</v-card-title> </v-card-title>
<v-card-text> <v-card-text>
{% trans "Select login option" %} {% trans "Select option" %}
<v-list-item> <v-list-item>
<v-list-item-content> <v-list-item-content>
+2
View File
@@ -1,6 +1,8 @@
{% include "vue/plugins.js" %} {% include "vue/plugins.js" %}
{% if settings.DEBUG %}
Vue.config.devtools = true Vue.config.devtools = true
{% endif %}
Vue.use(VueRouter) Vue.use(VueRouter)
Vue.use(Vuex) Vue.use(Vuex)
+1 -1
View File
@@ -12,6 +12,6 @@ const approuter = new Vue({
vuetify: new Vuetify(), vuetify: new Vuetify(),
el: "#main", el: "#main",
data: { data: {
dialog: true, dialog: false,
}, },
}) })