Skip to content

Commit 445e075

Browse files
committed
generate README.md from documentation folder
1 parent 1f8fe6a commit 445e075

40 files changed

+5384
-854
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ deploy.cmd
88
documentation/encryptcontent.key
99
documentation/canary.py
1010
documentation/theme_override/assets/
11+
documentation/__pycache__/

README.md

Lines changed: 939 additions & 853 deletions
Large diffs are not rendered by default.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<div id="mkdocs-encrypted-content" style="display:none">{{ ciphertext_bundle }}</div>
2+
<div id="mkdocs-decrypted-content">
3+
<form id="mkdocs-decrypt-form"{% if form_class %} class="{{ form_class }}"{% endif %}>
4+
<h1>{{ summary }}</h1>
5+
{% if encryption_info_message %}<p>{{ encryption_info_message }}</p>{% endif %}
6+
<div class="w3-row-padding" style="padding-left: 0px;">
7+
{%- if obfuscate %}
8+
<input type="hidden" id="mkdocs-content-password" value="{{ obfuscate_password }}" />
9+
{%- else %}
10+
{%- if uname %}
11+
<div class="w3-third">
12+
<input{% if input_class %} class="{{ input_class }}"{% endif %} type="text" id="mkdocs-content-user" placeholder="{{ placeholder_user }}" />
13+
</div>
14+
{%- endif %}
15+
<div class="w3-third">
16+
<input{% if input_class %} class="{{ input_class }}"{% endif %} type="password" id="mkdocs-content-password" placeholder="{{ placeholder }}" />
17+
</div>
18+
{%- endif %}
19+
{%- if password_button %}
20+
<div class="w3-third">
21+
<button{% if button_class %} class="{{ button_class }}"{% endif %} id="mkdocs-decrypt-button">{{ password_button_text }}</button>
22+
</div>
23+
{% endif -%}
24+
</div>
25+
<p id="mkdocs-decrypt-msg"></p>
26+
</form>
27+
</div>
28+
29+
<script type="text/javascript">
30+
var encryptcontent_id = "{{ encryptcontent_id }}";
31+
var encryptcontent_path = "{{ encryptcontent_path }}";
32+
var decryption_failure_message = {{ decryption_failure_message }};
33+
var encryptcontent_keystore = {{ encryptcontent_keystore }};
34+
var encryptcontent_obfuscate = {{ obfuscate }};
35+
{% if inject_something %}var inject_something = {{ inject_something }};{% endif -%}
36+
{% if delete_something %}var delete_something = "{{ delete_something }}";{%- endif %}
37+
</script>
38+
{% for library in js_libraries %}
39+
<script type="text/javascript" src="{{ library }}"></script>
40+
{%- endfor %}
41+
<script type="text/javascript" src="{{ base_path }}assets/javascripts/decrypt-contents.js" defer></script>

documentation/docs/explanations.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# How does this work?
2+
3+
For every `password` and for every `level` we generate a random 256 bit key.
4+
This key will be used for AES-256 encryption on every page with the same `password` or same `level`.
5+
Optionally search entries and [encrypt something](#encrypt-something)<!--todo--> on that page are also encrypted with the same key.
6+
7+
This random secret key (needed for deciphering the pages content) is then encrypted to a keystore with another key that is derived from
8+
the defined credentials (a password or a username/password pair).
9+
If the credential is reused on another `level`, then its random key is also encrypted in the same keystore.
10+
The function to derive the key to the keystore (PBKDF2) can be adjusted in calculation cycles
11+
to hopefully throttle the number of passwords a brute force attacker is able to try per second.
12+
13+
![](img/howitworks.svg)
14+
15+
#### AES
16+
17+
Is a symmetric encryption algorithm. Symmetric means it uses the same secret key for encryption and decryption.
18+
The size of the secret keys used in this plugin is 256 Bit (or 32 Bytes).\
19+
Additionally a random (16 Bytes) initialization vector (called IV) is used (and added to the cyphertext)
20+
to make sure the same plaintext will always result in a different cyphertext even if the same secret key is used
21+
for encryption.
22+
23+
#### KDF
24+
25+
A **K**ey **D**erivation **F**unction a one-way key generation algorithm,
26+
so it is not possible to reverse the function to retrieve a lost password.\
27+
The password is salted, meaning if two people used the same password it would lead to different keys (similar to IV in AES).\
28+
It is equipped with adjustable difficulty `kdf_pow`. The difficulty has significant weight on the build time and also
29+
the time needed to decrypt a page, so it shouldn't be increased to any value.\
30+
The algorithm used (PBKDF2) is not really state-of-the-art, meaning compared to better alternatives like bcrypt, scrypt or argon2
31+
it is easier to crack with hardware acceleration, however it is supported in both crypto-js and webcrypto and
32+
is significantly better than MD5 (which was used on all versions prior to 3.x) for key derivation.\
33+
The secret key derived from PBKDF2 is used to decrypt the keystore containing different keys to decrypt the content.
34+
35+
# A word on password strength
36+
37+
The strength of a password can be measured in entropy or "possibilities to try" (for a brute force attacker).
38+
39+
\[
40+
S = C^{L} * T
41+
\]
42+
43+
**S** is the time it takes to try all possibilities (on average a password is found after trying half the possibilities),
44+
**C** being the number of different characters the password is made of, **L** being the length of the password
45+
and **T** the time it takes to try one password.
46+
47+
For example take a tree character password with just lower case letters like "abc".
48+
The number of lower case letters is 26, so a three character password leads to 26 * 26 * 26 = 17 576 possibilities to try.
49+
50+
Now take a three character password which also includes upper case letters like "aBc".
51+
The number of possibilities per character doubles to 52, so the three character password leads to 52 * 52 * 52 = 140 608 possibilities.
52+
So compared to "abc" we got **eight times** more entropy in this case.
53+
54+
So what happens if we add one character and still only use lower case letters, like "abcd"?
55+
It's 26^4 = 456 976 with a four character password, that's **26 times** more entropy compared to only using three lower case characters.
56+
57+
It's easier to get higher entropy by increasing password size, than with adding more different characters or symbols.
58+
An attacker could also have watched (or heard) you type the password (paying attention to the use of the shift key,
59+
space bar or numeric keypad) and this way cross out character that you couldn't possibly have used.
60+
61+
So, to put it mildly: Every web page that forces you to use at least lower/upper case AND a number AND a symbol,
62+
BUT only forces you to use eight characters of password size is not steering you to the right measures to gain entropy.
63+
64+
But, to be fair: A web page can take measures to seriously throttle the passwords try-able per second on the server side
65+
or f.ex. use captchas after the third failed try. Although there were and most likely will be bad or failed examples of those measures.
66+
67+
This Mkdocs plugin can currently only take additional counter-measures to brute force attacks in form of PBKDF2.
68+
PBKDF2 itself is more or less obsolete, because it got no defense against being run in parallel, so we should
69+
consider it to not having a big effect on **T**.
70+
Other options like bcrypt, scrypt od argon2 exist, but pbkdf2 is the only option
71+
currently available in crypto-js and webcrypto. However there may be limitations that render the other option impractical,
72+
like logner build times and high computing or memory requirements on mobile devices.
73+
So you should really be interested in choosing a **long** password or pass phrases
74+
(read the [Diceware article on Wikipedia](https://en.wikipedia.org/wiki/Diceware), [xkcd webcomic on password strength](https://xkcd.com/936/)
75+
and [this blogpost](https://blog.benpri.me/blog/2019/01/13/why-you-shouldnt-be-using-bcrypt-and-scrypt/)).
76+
77+
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
title: Features
2+
3+
# Features
4+
5+
### Override default templates
6+
7+
Related to [issue #32](https://github.com/unverbuggt/mkdocs-encryptcontent-plugin/issues/32)
8+
9+
You can override the default templates with your own templates by providing an actual replacement
10+
path in the `html_template_path` *(HTML)* and `js_template_path` *(JS)* directives.
11+
Overridden templates **completely** replace the default templates. You **must** therefore copy the
12+
default templates as a starting point to keep this plugin working.
13+
14+
```yaml
15+
plugins:
16+
- encryptcontent:
17+
html_template_path: "/root/real/path/mkdocs_poc/my_html_template.html"
18+
js_template_path: "/root/real/path/mkdocs_poc/my_js_template.js"
19+
form_class: 'md-content__inner md-typeset'
20+
input_class: 'md-input'
21+
button_class: 'md-button md-button--primary'
22+
```
23+
24+
Use `form_class`, `input_class` and `button_class` to optionally set a CSS class for the password input field and the button.
25+
26+
When overriding the default templates, you can add and use your own Jinja variables
27+
and enrich your template, by defining `html_extra_vars` and `js_extra_vars` directives in key/value format.
28+
Added values can be used in your Jinja templates via the variable `extra`.
29+
30+
```yaml
31+
plugins:
32+
- encryptcontent:
33+
html_extra_vars:
34+
my_extra: "extra value"
35+
<key>: <value>
36+
js_extra_vars:
37+
my_extra: "extra value"
38+
<key>: <value>
39+
```
40+
41+
For example, you can modify your HTML template, to add a new title with your own text variable.
42+
43+
```jinja
44+
[ ... ]
45+
<h2>{{ extra.my_extra }}</h2>
46+
[ ... ]
47+
```
48+
49+
> **NOTE** Issues related to template override will not be addressed.
50+
51+
### Add button
52+
53+
Add `password_button: True` in plugin configuration variable, to add a button to the right of the password field.
54+
55+
When enabled, it allows to decrypt the content just like the classic keypress ENTER.
56+
57+
Optionally, you can add `password_button_text: 'custom_text_button'` to customize the button text.
58+
59+
```yaml
60+
plugins:
61+
- encryptcontent:
62+
password_button: True
63+
password_button_text: 'custom_text_button'
64+
```
65+
66+
### Tag encrypted page
67+
68+
> **Enable by default**
69+
70+
Related to [issue #7](https://github.com/unverbuggt/mkdocs-encryptcontent-plugin/issues/7)
71+
72+
This feature adds an additional attribute `encrypted` with value `True` to the mkdocs type `mkdocs.nav.page` object.
73+
74+
You can add `tag_encrypted_page: False` in plugin configuration, to disable tagging of encrypted pages.
75+
76+
When enabled, it is possible to use the `encrypted` attribute in the jinja template of your theme, as a condition to perform custom modification.
77+
78+
```jinja
79+
{%- for nav_item in nav %}
80+
{% if nav_item.encrypted %}
81+
<!-- Do something -->
82+
{% endif %}
83+
{%- endfor %}
84+
```
85+
86+
For example, you can use conditional check to add a custom class:
87+
88+
```jinja
89+
<a {% if nav_item.encrypted %}class="mkdocs-encrypted-class"{% endif %}href="{{ nav_item.url|url }}">{{ nav_item.title }}</a>
90+
```
91+
92+
### Remember password
93+
94+
Related to [issue #6](https://github.com/unverbuggt/mkdocs-encryptcontent-plugin/issues/6)
95+
96+
By default the plugin will save the decrypted AES keys to session storage of the browser (can be disabled by setting `remember_keys: False`).
97+
This is enabled for convenience, so you are able to browse between multiple encrypted pages without having to re-enter the password.
98+
99+
Additionally it is possible to save the entered user/password in session storage (setting `remember_password: True`). Use this for
100+
additional convenience during `mkdocs serve`, because the AES key are regenerated every time MkDocs starts
101+
(rendering the old ones invalid and requiring to re-enter a valid credential again).
102+
103+
To avoid problems when multiple sites are hosted within the same domain, it is possible to customize the name of
104+
the keys saved to storage with `remember_prefix`.
105+
106+
> **This feature is not really secure !** decryption keys are store in clear text inside session storage.
107+
>
108+
> Instead of using these features, I recommend to use a password manager with its browser plugin.
109+
> For example **KeepassXC** allows you to detect the password field
110+
> `mkdocs-content-password` and fill it automatically in a much more secure way.
111+
112+
It is also possible to save the used credentials permanently to local storage (setting `session_storage: False`), but
113+
this should only be done for testing purposes. The local storage of a browser is most likely readable
114+
for every other process that can access the file system.
115+
116+
The session storage however should only be located in memory and be forgotten after the browser tab is closed.
117+
118+
```yaml
119+
plugins:
120+
- encryptcontent:
121+
remember_keys : True
122+
remember_password: False
123+
remember_prefix: secretsite_
124+
```
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
title: Javascript extensions
2+
3+
## Javascript extensions
4+
5+
### Reload user-defined scripts
6+
7+
Related to [issue #14](https://github.com/unverbuggt/mkdocs-encryptcontent-plugin/issues/14)
8+
9+
You can set `reload_scripts:` in your `mkdocs.yml` with list of script sources
10+
or script ids, to reload and execute some js lib after decryption process.
11+
12+
```yaml
13+
plugins:
14+
- encryptcontent:
15+
reload_scripts:
16+
- 'js/example.js'
17+
- '#autoexec'
18+
```
19+
20+
It is also possible to reload a script id like `<script id="autoexec">console.log('test');</script>`
21+
that was encrypted within the page
22+
(related to [issue #30](https://github.com/unverbuggt/mkdocs-encryptcontent-plugin/issues/30)).
23+
24+
25+
### HighlightJS support
26+
27+
> **Enable by default**
28+
29+
If HighlightJS module is detected in your theme to improve code color rendering, reload renderer after
30+
decryption process.
31+
If HighlightJS module is not correctly detected, you can force the detection by adding `hljs: True`
32+
on the plugin configuration
33+
or set `hljs: False` to disable this feature.
34+
35+
When enabled the following part of the template is added to force reloading decrypted content.
36+
37+
```jinja
38+
{% if hljs %}
39+
document.getElementById("mkdocs-decrypted-content").querySelectorAll('pre code').forEach((block) => {
40+
hljs.highlightElement(block);
41+
});
42+
{% endif %}
43+
```
44+
45+
46+
### Arithmatex support
47+
48+
> **Enable by default**
49+
50+
Related to [issue #12](https://github.com/unverbuggt/mkdocs-encryptcontent-plugin/issues/12)
51+
52+
If Arithmatex markdown extension is detected in your markdown extensions to improve math equations rendering,
53+
reload renderer after decryption process.
54+
If the Arithmatex markdown extension is not correctly detected, you can force the detection by adding
55+
`arithmatex: True` on the plugin configuration
56+
or set `arithmatex: False` to disable this feature.
57+
58+
When enabled, the following part of the template is added to force math equations rendering on decrypted content.
59+
60+
```jinja
61+
{% if arithmatex %}
62+
MathJax.typesetPromise()
63+
{% endif %}
64+
```
65+
66+
> **NOTE** It has been tested in Arithmatex `generic` mode only.
67+
68+
69+
### Mermaid2 support
70+
71+
> **Enable by default**
72+
73+
Related to [issue #22](https://github.com/unverbuggt/mkdocs-encryptcontent-plugin/issues/22)
74+
75+
If mermaid2 plugin is detected in your configuration to generate graph from text, reload renderer after
76+
decryption process.
77+
If the Mermaid2 plugin is not correctly detected, you can force the detection by adding `mermaid2: True`
78+
on the plugin configuration
79+
or set `mermaid2: False` to disable this feature.
80+
81+
When enabled, the following part of the template is added to force graph rendering on decrypted content.
82+
83+
```jinja
84+
{% if mermaid2 %}
85+
mermaid.contentLoaded();
86+
{% endif %}
87+
```
88+
89+
> **NOTE** it currently only works with mermaid version < 10. Also encryptcontent needs to be injected,
90+
> because the mermaid2 plugin otherwise won't detect the page content correctly.
91+
92+
activate the plugin like this:
93+
94+
```yaml
95+
plugins:
96+
- mermaid2:
97+
version: 9.4.3
98+
99+
markdown_extensions:
100+
- pymdownx.blocks.html
101+
```
102+
103+
Example usage:
104+
105+
````
106+
password: 1234
107+
inject_id: inject
108+
109+
110+
/// html | div#inject
111+
112+
```mermaid
113+
graph LR
114+
hello --> world
115+
world --> again
116+
again --> hello
117+
```
118+
119+
///
120+
````
121+

0 commit comments

Comments
 (0)