287 lines
12 KiB
HTML
287 lines
12 KiB
HTML
{% load i18n static %}
|
|
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>{% block title %}{% endblock %}</title>
|
|
|
|
<link rel="shortcut icon" href="{% static 'img/favicon.png' %}">
|
|
|
|
<script src="{% static "vue/index.js" %}"></script>
|
|
<script src="{% static "sweetalert2/index" %}"></script>
|
|
<script src="{% static "vue-resource/index" %}"></script>
|
|
<script src="{% static "js.cookie.min/index.js" %}"></script>
|
|
<script src="{% static "vue-router/index.js" %}"></script>
|
|
|
|
<link href="{% static "bootstrap.min/index.css" %}" rel="stylesheet">
|
|
<script src="{% static "bootstrap.bundle.min/index.js" %}"></script>
|
|
<link href="{% static "bootswatch.min.css/index.css" %}" rel="stylesheet">
|
|
|
|
<script src="{% url 'reverse_js' %}" type="text/javascript"></script>
|
|
|
|
<link href="{% static "vuetify.min/index.css" %}" rel="stylesheet">
|
|
<script src="{% static "vuetify/index.js" %}"></script>
|
|
|
|
<script src="{% static "vuex/index.js" %}"></script>
|
|
|
|
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css" rel="stylesheet">
|
|
|
|
<link href="{% static "jetbrains-mono/index" %}">
|
|
<style>
|
|
.font {
|
|
font-family: 'JetBrains Mono', sans-serif;
|
|
}
|
|
</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>
|
|
<body>
|
|
<v-app class="font">
|
|
<div id="main" data-app>
|
|
|
|
<v-content>
|
|
<v-container class="container-xs">
|
|
|
|
<v-row justify="center">
|
|
|
|
<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-title class="text-h5">
|
|
{% trans "Login / Register" %}
|
|
</v-card-title>
|
|
|
|
<v-card-text>
|
|
|
|
{% trans "Select option" %}
|
|
|
|
<v-list-item>
|
|
<v-list-item-content>
|
|
<v-btn tile color="primary" dark class="mb-2" href="{% url 'social:begin' 'github' %}">
|
|
{% trans "Github" %}
|
|
</v-btn>
|
|
</v-list-item-content>
|
|
</v-list-item>
|
|
|
|
</v-card-text>
|
|
|
|
</v-card>
|
|
</v-dialog>
|
|
</v-row>
|
|
</v-container>
|
|
|
|
</v-content>
|
|
|
|
</div>
|
|
</v-app>
|
|
|
|
<script type="text/javascript">
|
|
{% include 'scripts.js' %}
|
|
</script>
|
|
|
|
{% for name, path in templates.items %}
|
|
<script type="text/x-template" id="{{ name }}">
|
|
{% include path %}
|
|
</script>
|
|
{% endfor %}
|
|
|
|
<script type="text/javascript">
|
|
{% include "vue/login.js" %}
|
|
</script>
|
|
|
|
<script type="text/javascript">
|
|
function refresh_csrftoken() {
|
|
var csrftoken = Cookies.get('csrftoken');
|
|
Vue.http.headers.common['X-CSRFTOKEN'] = csrftoken;
|
|
}
|
|
|
|
refresh_csrftoken();
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|
|
|