Compare commits
2 commits
e9460c86fe
...
1223888e36
| Author | SHA1 | Date | |
|---|---|---|---|
| 1223888e36 | |||
| e92401bb76 |
14 changed files with 944 additions and 17 deletions
|
|
@ -28,6 +28,9 @@ header_blog = "Blog"
|
|||
header_links = "Links"
|
||||
header_reads = "Reads"
|
||||
blog_published_on_date = "Posted by a human on"
|
||||
footer_mail = "Electronic missive:<br/>tuxmain ât zettascript ðøt org"
|
||||
footer_hosted = "Hosted in Bordeaux, France."
|
||||
footer_generated = "Site generated with"
|
||||
|
||||
[extra.tr.eo]
|
||||
sitename = "txmn.tk"
|
||||
|
|
@ -36,6 +39,9 @@ header_blog = "Blogo"
|
|||
header_links = "Ligiloj"
|
||||
header_reads = "Legaĵoj"
|
||||
blog_published_on_date = "Publikiĝita home je la"
|
||||
footer_mail = "Retadreso:<br/>tuxmain ĉe zettascript punkto org"
|
||||
footer_hosted = "Gastigata en Bordozo, en Francujo."
|
||||
footer_generated = "Retejo konstruita per"
|
||||
|
||||
[extra.tr.fr]
|
||||
sitename = "txmn.tk"
|
||||
|
|
@ -44,6 +50,9 @@ header_blog = "Blog"
|
|||
header_links = "Liens"
|
||||
header_reads = "Lectures"
|
||||
blog_published_on_date = "Publié par un humain le"
|
||||
footer_mail = "Courrier électronique :<br/>tuxmain çhĕz zettascript pøiñt org"
|
||||
footer_hosted = "Hébergé à Bordeaux en France."
|
||||
footer_generated = "Site généré avec"
|
||||
|
||||
[languages.en]
|
||||
generate_feeds = true
|
||||
|
|
|
|||
28
content/blog/electronics-providers.md
Normal file
28
content/blog/electronics-providers.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
+++
|
||||
title = "Electronics"
|
||||
date = 2025-10-30
|
||||
description = "Electronic providers"
|
||||
insert_anchor_links = "left"
|
||||
draft = true
|
||||
[taxonomies]
|
||||
tags = ["electronics"]
|
||||
+++
|
||||
|
||||
* https://www.a4.fr/
|
||||
* Adafruit
|
||||
* Arduino
|
||||
* https://composant-electronique.fr/
|
||||
* Eckstein
|
||||
* https://electronics-diy.com/store.php
|
||||
* Farnell
|
||||
* [Gotronic](https://www.gotronic.fr/)
|
||||
* Lextronic
|
||||
* Reichelt
|
||||
* Robotshop
|
||||
* https://rs-online.com/
|
||||
* Seeed Studio
|
||||
* Semageek
|
||||
* Soldered
|
||||
* Sparkfun
|
||||
* https://thepihut.com/
|
||||
* Waveshare
|
||||
95
content/blog/flash-filesystem-encryption/cbc.svg
Normal file
95
content/blog/flash-filesystem-encryption/cbc.svg
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="372" height="172" viewBox="0 0 372 172">
|
||||
<!-- Generated by https://txmn.tk/blog/flash-filesystem-encryption/graph.py -->
|
||||
<!-- Image released under license CC0 (public domain) -->
|
||||
<title>CBC</title>
|
||||
<style>
|
||||
text.t {
|
||||
color: #000;
|
||||
fill: #000;
|
||||
text-anchor: middle;
|
||||
dominant-baseline: central;
|
||||
font-size: 16px;
|
||||
font-family: "Libertinus Sans";
|
||||
}
|
||||
circle, line, rect, path.s {
|
||||
stroke: #000;
|
||||
stroke-width: 2px;
|
||||
fill: none;
|
||||
}
|
||||
path.f {
|
||||
stroke: none;
|
||||
fill: #000;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
text.t {
|
||||
color: #ddd;
|
||||
fill: #ddd;
|
||||
}
|
||||
circle, line, rect, path.s {
|
||||
stroke: #ddd;
|
||||
}
|
||||
path.f {
|
||||
fill: #ddd;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<text class="t" x="64" y="16">P</text>
|
||||
<circle r="8" cx="64" cy="56"/>
|
||||
<line x1="56" y1="56" x2="72" y2="56"/>
|
||||
<line x1="64" y1="48" x2="64" y2="64"/>
|
||||
<text class="t" x="16" y="96">K</text>
|
||||
<rect x="48" y="80" width="32" height="32"/>
|
||||
<text class="t" x="64" y="96">E</text>
|
||||
<text class="t" x="64" y="144">C</text>
|
||||
<line x1="64" y1="28" x2="64.0" y2="40.0"/>
|
||||
<path class="f" transform="translate(64,48) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="64" y1="64" x2="64.0" y2="72.0"/>
|
||||
<path class="f" transform="translate(64,80) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="28" y1="96" x2="40.0" y2="96.0"/>
|
||||
<path class="f" transform="translate(48,96) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="64" y1="112" x2="64.0" y2="124.0"/>
|
||||
<path class="f" transform="translate(64,132) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<text class="t" x="16" y="56">IV</text>
|
||||
<line x1="28" y1="56" x2="48.0" y2="56.0"/>
|
||||
<path class="f" transform="translate(56,56) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<path class="s" d="M72 144L96 144L96 56L144.0 56.0"/>
|
||||
<path class="f" transform="translate(152,56) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<text class="t" x="160" y="16">P</text>
|
||||
<circle r="8" cx="160" cy="56"/>
|
||||
<line x1="152" y1="56" x2="168" y2="56"/>
|
||||
<line x1="160" y1="48" x2="160" y2="64"/>
|
||||
<text class="t" x="112" y="96">K</text>
|
||||
<rect x="144" y="80" width="32" height="32"/>
|
||||
<text class="t" x="160" y="96">E</text>
|
||||
<text class="t" x="160" y="144">C</text>
|
||||
<line x1="160" y1="28" x2="160.0" y2="40.0"/>
|
||||
<path class="f" transform="translate(160,48) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="160" y1="64" x2="160.0" y2="72.0"/>
|
||||
<path class="f" transform="translate(160,80) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="124" y1="96" x2="136.0" y2="96.0"/>
|
||||
<path class="f" transform="translate(144,96) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="160" y1="112" x2="160.0" y2="124.0"/>
|
||||
<path class="f" transform="translate(160,132) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<path class="s" d="M168 144L192 144L192 56L240.0 56.0"/>
|
||||
<path class="f" transform="translate(248,56) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<text class="t" x="256" y="16">P</text>
|
||||
<circle r="8" cx="256" cy="56"/>
|
||||
<line x1="248" y1="56" x2="264" y2="56"/>
|
||||
<line x1="256" y1="48" x2="256" y2="64"/>
|
||||
<text class="t" x="208" y="96">K</text>
|
||||
<rect x="240" y="80" width="32" height="32"/>
|
||||
<text class="t" x="256" y="96">E</text>
|
||||
<text class="t" x="256" y="144">C</text>
|
||||
<line x1="256" y1="28" x2="256.0" y2="40.0"/>
|
||||
<path class="f" transform="translate(256,48) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="256" y1="64" x2="256.0" y2="72.0"/>
|
||||
<path class="f" transform="translate(256,80) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="220" y1="96" x2="232.0" y2="96.0"/>
|
||||
<path class="f" transform="translate(240,96) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="256" y1="112" x2="256.0" y2="124.0"/>
|
||||
<path class="f" transform="translate(256,132) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<path class="s" d="M264 144L288 144L288 56L336.0 56.0"/>
|
||||
<path class="f" transform="translate(344,56) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<text class="t" x="356" y="56">···</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
94
content/blog/flash-filesystem-encryption/ctr.svg
Normal file
94
content/blog/flash-filesystem-encryption/ctr.svg
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="288" height="172" viewBox="0 0 288 172">
|
||||
<!-- Generated by https://txmn.tk/blog/flash-filesystem-encryption/graph.py -->
|
||||
<!-- Image released under license CC0 (public domain) -->
|
||||
<title>CTR</title>
|
||||
<style>
|
||||
text.t {
|
||||
color: #000;
|
||||
fill: #000;
|
||||
text-anchor: middle;
|
||||
dominant-baseline: central;
|
||||
font-size: 16px;
|
||||
font-family: "Libertinus Sans";
|
||||
}
|
||||
circle, line, rect, path.s {
|
||||
stroke: #000;
|
||||
stroke-width: 2px;
|
||||
fill: none;
|
||||
}
|
||||
path.f {
|
||||
stroke: none;
|
||||
fill: #000;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
text.t {
|
||||
color: #ddd;
|
||||
fill: #ddd;
|
||||
}
|
||||
circle, line, rect, path.s {
|
||||
stroke: #ddd;
|
||||
}
|
||||
path.f {
|
||||
fill: #ddd;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<text class="t" x="64" y="16">N||0</text>
|
||||
<text class="t" x="16" y="64">K</text>
|
||||
<rect x="48" y="48" width="32" height="32"/>
|
||||
<text class="t" x="64" y="64">E</text>
|
||||
<text class="t" x="16" y="112">P</text>
|
||||
<circle r="8" cx="64" cy="112"/>
|
||||
<line x1="56" y1="112" x2="72" y2="112"/>
|
||||
<line x1="64" y1="104" x2="64" y2="120"/>
|
||||
<text class="t" x="64" y="152">C</text>
|
||||
<line x1="64" y1="28" x2="64.0" y2="40.0"/>
|
||||
<path class="f" transform="translate(64,48) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="28" y1="64" x2="40.0" y2="64.0"/>
|
||||
<path class="f" transform="translate(48,64) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="64" y1="80" x2="64.0" y2="96.0"/>
|
||||
<path class="f" transform="translate(64,104) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="28" y1="112" x2="48.0" y2="112.0"/>
|
||||
<path class="f" transform="translate(56,112) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="64" y1="120" x2="64.0" y2="132.0"/>
|
||||
<path class="f" transform="translate(64,140) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<text class="t" x="160" y="16">N||1</text>
|
||||
<text class="t" x="112" y="64">K</text>
|
||||
<rect x="144" y="48" width="32" height="32"/>
|
||||
<text class="t" x="160" y="64">E</text>
|
||||
<text class="t" x="112" y="112">P</text>
|
||||
<circle r="8" cx="160" cy="112"/>
|
||||
<line x1="152" y1="112" x2="168" y2="112"/>
|
||||
<line x1="160" y1="104" x2="160" y2="120"/>
|
||||
<text class="t" x="160" y="152">C</text>
|
||||
<line x1="160" y1="28" x2="160.0" y2="40.0"/>
|
||||
<path class="f" transform="translate(160,48) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="124" y1="64" x2="136.0" y2="64.0"/>
|
||||
<path class="f" transform="translate(144,64) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="160" y1="80" x2="160.0" y2="96.0"/>
|
||||
<path class="f" transform="translate(160,104) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="124" y1="112" x2="144.0" y2="112.0"/>
|
||||
<path class="f" transform="translate(152,112) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="160" y1="120" x2="160.0" y2="132.0"/>
|
||||
<path class="f" transform="translate(160,140) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<text class="t" x="256" y="16">N||2</text>
|
||||
<text class="t" x="208" y="64">K</text>
|
||||
<rect x="240" y="48" width="32" height="32"/>
|
||||
<text class="t" x="256" y="64">E</text>
|
||||
<text class="t" x="208" y="112">P</text>
|
||||
<circle r="8" cx="256" cy="112"/>
|
||||
<line x1="248" y1="112" x2="264" y2="112"/>
|
||||
<line x1="256" y1="104" x2="256" y2="120"/>
|
||||
<text class="t" x="256" y="152">C</text>
|
||||
<line x1="256" y1="28" x2="256.0" y2="40.0"/>
|
||||
<path class="f" transform="translate(256,48) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="220" y1="64" x2="232.0" y2="64.0"/>
|
||||
<path class="f" transform="translate(240,64) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="256" y1="80" x2="256.0" y2="96.0"/>
|
||||
<path class="f" transform="translate(256,104) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="220" y1="112" x2="240.0" y2="112.0"/>
|
||||
<path class="f" transform="translate(248,112) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="256" y1="120" x2="256.0" y2="132.0"/>
|
||||
<path class="f" transform="translate(256,140) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
70
content/blog/flash-filesystem-encryption/ecb.svg
Normal file
70
content/blog/flash-filesystem-encryption/ecb.svg
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="288" height="128" viewBox="0 0 288 128">
|
||||
<!-- Generated by https://txmn.tk/blog/flash-filesystem-encryption/graph.py -->
|
||||
<!-- Image released under license CC0 (public domain) -->
|
||||
<title>ECB</title>
|
||||
<style>
|
||||
text.t {
|
||||
color: #000;
|
||||
fill: #000;
|
||||
text-anchor: middle;
|
||||
dominant-baseline: central;
|
||||
font-size: 16px;
|
||||
font-family: "Libertinus Sans";
|
||||
}
|
||||
circle, line, rect, path.s {
|
||||
stroke: #000;
|
||||
stroke-width: 2px;
|
||||
fill: none;
|
||||
}
|
||||
path.f {
|
||||
stroke: none;
|
||||
fill: #000;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
text.t {
|
||||
color: #ddd;
|
||||
fill: #ddd;
|
||||
}
|
||||
circle, line, rect, path.s {
|
||||
stroke: #ddd;
|
||||
}
|
||||
path.f {
|
||||
fill: #ddd;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<text class="t" x="64" y="16">P</text>
|
||||
<text class="t" x="16" y="64">K</text>
|
||||
<rect x="48" y="48" width="32" height="32"/>
|
||||
<text class="t" x="64" y="64">E</text>
|
||||
<text class="t" x="64" y="112">C</text>
|
||||
<line x1="64" y1="28" x2="64.0" y2="40.0"/>
|
||||
<path class="f" transform="translate(64,48) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="28" y1="64" x2="40.0" y2="64.0"/>
|
||||
<path class="f" transform="translate(48,64) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="64" y1="80" x2="64.0" y2="92.0"/>
|
||||
<path class="f" transform="translate(64,100) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<text class="t" x="160" y="16">P</text>
|
||||
<text class="t" x="112" y="64">K</text>
|
||||
<rect x="144" y="48" width="32" height="32"/>
|
||||
<text class="t" x="160" y="64">E</text>
|
||||
<text class="t" x="160" y="112">C</text>
|
||||
<line x1="160" y1="28" x2="160.0" y2="40.0"/>
|
||||
<path class="f" transform="translate(160,48) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="124" y1="64" x2="136.0" y2="64.0"/>
|
||||
<path class="f" transform="translate(144,64) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="160" y1="80" x2="160.0" y2="92.0"/>
|
||||
<path class="f" transform="translate(160,100) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<text class="t" x="256" y="16">P</text>
|
||||
<text class="t" x="208" y="64">K</text>
|
||||
<rect x="240" y="48" width="32" height="32"/>
|
||||
<text class="t" x="256" y="64">E</text>
|
||||
<text class="t" x="256" y="112">C</text>
|
||||
<line x1="256" y1="28" x2="256.0" y2="40.0"/>
|
||||
<path class="f" transform="translate(256,48) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="220" y1="64" x2="232.0" y2="64.0"/>
|
||||
<path class="f" transform="translate(240,64) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="256" y1="80" x2="256.0" y2="92.0"/>
|
||||
<path class="f" transform="translate(256,100) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
256
content/blog/flash-filesystem-encryption/graph.py
Normal file
256
content/blog/flash-filesystem-encryption/graph.py
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
#!/usr/bin/python3
|
||||
"""
|
||||
GNU AGPL v3, CopyLeft 2025 Pascal Engélibert [(why copyleft?)](https://txmn.tk/blog/why-copyleft/)
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License.
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
||||
You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
"""
|
||||
|
||||
import math
|
||||
|
||||
ARGS = {
|
||||
"BLACK": "#000",
|
||||
"WHITE": "#ddd",
|
||||
}
|
||||
SVG = """\
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="{w}" height="{h}" viewBox="0 0 {w} {h}">
|
||||
<!-- Generated by https://txmn.tk/blog/flash-filesystem-encryption/graph.py -->
|
||||
<!-- Image released under license CC0 (public domain) -->
|
||||
<title>{title}</title>
|
||||
<style>
|
||||
text.t {{
|
||||
color: {BLACK};
|
||||
fill: {BLACK};
|
||||
text-anchor: middle;
|
||||
dominant-baseline: central;
|
||||
font-size: 16px;
|
||||
font-family: "Libertinus Sans";
|
||||
}}
|
||||
circle, line, rect, path.s {{
|
||||
stroke: {BLACK};
|
||||
stroke-width: 2px;
|
||||
fill: none;
|
||||
}}
|
||||
path.f {{
|
||||
stroke: none;
|
||||
fill: {BLACK};
|
||||
}}
|
||||
@media (prefers-color-scheme: dark) {{
|
||||
text.t {{
|
||||
color: {WHITE};
|
||||
fill: {WHITE};
|
||||
}}
|
||||
circle, line, rect, path.s {{
|
||||
stroke: {WHITE};
|
||||
}}
|
||||
path.f {{
|
||||
fill: {WHITE};
|
||||
}}
|
||||
}}
|
||||
</style>
|
||||
{body}
|
||||
</svg>
|
||||
"""
|
||||
|
||||
class Block:
|
||||
def __init__(self, svg, x, y, r):
|
||||
self.svg = svg
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.r = r
|
||||
|
||||
def to(self, block):
|
||||
if self.x == block.x:
|
||||
if self.y > block.y:
|
||||
self.svg.t += arrow(self.x, self.y-self.r, block.x, block.y+block.r)
|
||||
else:
|
||||
self.svg.t += arrow(self.x, self.y+self.r, block.x, block.y-block.r)
|
||||
elif self.y == block.y:
|
||||
if self.x > block.x:
|
||||
self.svg.t += arrow(self.x-self.r, self.y, block.x+block.r, block.y)
|
||||
else:
|
||||
self.svg.t += arrow(self.x+self.r, self.y, block.x-block.r, block.y)
|
||||
|
||||
XOR_R = 8
|
||||
ENCRYPT_SIZE = 32
|
||||
class Svg:
|
||||
def __init__(self):
|
||||
self.t = ""
|
||||
|
||||
def xor(self, x, y):
|
||||
xl = x-XOR_R
|
||||
xr = x+XOR_R
|
||||
yt = y-XOR_R
|
||||
yb = y+XOR_R
|
||||
self.t += f"""\
|
||||
<circle r="{XOR_R}" cx="{x}" cy="{y}"/>
|
||||
<line x1="{xl}" y1="{y}" x2="{xr}" y2="{y}"/>
|
||||
<line x1="{x}" y1="{yt}" x2="{x}" y2="{yb}"/>
|
||||
"""
|
||||
return Block(self, x, y, XOR_R)
|
||||
|
||||
def encrypt(self, x, y):
|
||||
xl = x-ENCRYPT_SIZE//2
|
||||
yt = y-ENCRYPT_SIZE//2
|
||||
self.t += f"""\
|
||||
<rect x="{xl}" y="{yt}" width="{ENCRYPT_SIZE}" height="{ENCRYPT_SIZE}"/>
|
||||
<text class="t" x="{x}" y="{y}">E</text>
|
||||
"""
|
||||
return Block(self, x, y, ENCRYPT_SIZE//2)
|
||||
|
||||
def text(self, x, y, t):
|
||||
self.t += f"""\
|
||||
<text class="t" x="{x}" y="{y}">{t}</text>
|
||||
"""
|
||||
return Block(self, x, y, 12)
|
||||
|
||||
def square(self, x, y, t):
|
||||
xl = x-ENCRYPT_SIZE//2
|
||||
yt = y-ENCRYPT_SIZE//2
|
||||
self.t += f"""\
|
||||
<rect x="{xl}" y="{yt}" width="{ENCRYPT_SIZE}" height="{ENCRYPT_SIZE}"/>
|
||||
<text class="t" x="{x}" y="{y}">{t}</text>
|
||||
"""
|
||||
return Block(self, x, y, ENCRYPT_SIZE//2)
|
||||
|
||||
ARROW_TIP_START = 8
|
||||
ARROW_TIP_BACK = 12
|
||||
ARROW_TIP_R = 6
|
||||
ARROW_TIP = [
|
||||
(-ARROW_TIP_START, 0),
|
||||
(-ARROW_TIP_BACK, ARROW_TIP_R),
|
||||
(0, 0),
|
||||
(-ARROW_TIP_BACK, -ARROW_TIP_R),
|
||||
(-ARROW_TIP_START, 0),
|
||||
]
|
||||
def arrow(x1, y1, x2, y2):
|
||||
angle = math.degrees(math.atan2(y2-y1, x2-x1))
|
||||
# Stop the line before the end to prevent it from passing through the tip
|
||||
length = math.sqrt((x2-x1)**2+(y2-y1)**2)
|
||||
xend = x2 - (x2-x1)/length*ARROW_TIP_START
|
||||
yend = y2 - (y2-y1)/length*ARROW_TIP_START
|
||||
path = ""
|
||||
px = 0
|
||||
py = 0
|
||||
for p in ARROW_TIP:
|
||||
path += "l{} {}".format(p[0]-px, p[1]-py)
|
||||
px = p[0]
|
||||
py = p[1]
|
||||
return f"""\
|
||||
<line x1="{x1}" y1="{y1}" x2="{xend}" y2="{yend}"/>
|
||||
<path class="f" transform="translate({x2},{y2}) rotate({angle})" d="M0 0{path}z"/>
|
||||
"""
|
||||
|
||||
def arrow_path(points):
|
||||
x1 = points[-2][0]
|
||||
y1 = points[-2][1]
|
||||
x2 = points[-1][0]
|
||||
y2 = points[-1][1]
|
||||
angle = math.degrees(math.atan2(y2-y1, x2-x1))
|
||||
# Stop the line before the end to prevent it from passing through the tip
|
||||
length = math.sqrt((x2-x1)**2+(y2-y1)**2)
|
||||
xend = x2 - (x2-x1)/length*ARROW_TIP_START
|
||||
yend = y2 - (y2-y1)/length*ARROW_TIP_START
|
||||
points[-1] = (xend, yend)
|
||||
path_arrow = ""
|
||||
px = 0
|
||||
py = 0
|
||||
for p in ARROW_TIP:
|
||||
path_arrow += "l{} {}".format(p[0]-px, p[1]-py)
|
||||
px = p[0]
|
||||
py = p[1]
|
||||
path_line = "M" + "L".join(["{} {}".format(p[0], p[1]) for p in points])
|
||||
return f"""\
|
||||
<path class="s" d="{path_line}"/>
|
||||
<path class="f" transform="translate({x2},{y2}) rotate({angle})" d="M0 0{path_arrow}z"/>
|
||||
"""
|
||||
|
||||
def ecb():
|
||||
s = Svg()
|
||||
for i in range(3):
|
||||
P = s.text(96*i+64, 16, "P")
|
||||
K = s.text(96*i+16, 64, "K")
|
||||
E = s.encrypt(96*i+64, 64)
|
||||
C = s.text(96*i+64, 112, "C")
|
||||
P.to(E)
|
||||
K.to(E)
|
||||
E.to(C)
|
||||
return SVG.format(body=s.t, title="ECB", w=288, h=128, **ARGS)
|
||||
|
||||
def ctr():
|
||||
s = Svg()
|
||||
for i in range(3):
|
||||
S = s.text(96*i+64, 16, f"N||{i}")
|
||||
K = s.text(96*i+16, 64, "K")
|
||||
E = s.encrypt(96*i+64, 64)
|
||||
P = s.text(96*i+16, 112, "P")
|
||||
X = s.xor(96*i+64, 112)
|
||||
C = s.text(96*i+64, 152, "C")
|
||||
S.to(E)
|
||||
K.to(E)
|
||||
E.to(X)
|
||||
P.to(X)
|
||||
X.to(C)
|
||||
return SVG.format(body=s.t, title="CTR", w=288, h=172, **ARGS)
|
||||
|
||||
def cbc():
|
||||
s = Svg()
|
||||
for i in range(3):
|
||||
P = s.text(96*i+64, 16, "P")
|
||||
X = s.xor(96*i+64, 56)
|
||||
K = s.text(96*i+16, 96, "K")
|
||||
E = s.encrypt(96*i+64, 96)
|
||||
C = s.text(96*i+64, 144, "C")
|
||||
P.to(X)
|
||||
X.to(E)
|
||||
K.to(E)
|
||||
E.to(C)
|
||||
if i == 0:
|
||||
IV = s.text(96*i+16, 56, "IV")
|
||||
IV.to(X)
|
||||
s.t += arrow_path([(96*i+72,144), (96*i+96,144), (96*i+96,56), (96*(1+i)+56,56)])
|
||||
s.text(356, 56, "···")
|
||||
return SVG.format(body=s.t, title="CBC", w=372, h=172, **ARGS)
|
||||
|
||||
def xts():
|
||||
s = Svg()
|
||||
P = s.text(224, 16, "P")
|
||||
X1 = s.xor(224, 56)
|
||||
E1 = s.encrypt(224, 96)
|
||||
X2 = s.xor(224, 144)
|
||||
C = s.text(224, 180, "C")
|
||||
|
||||
K1 = s.text(176, 96, 'K<tspan dy="6">1</tspan>')
|
||||
|
||||
I = s.text(64, 16, "i")
|
||||
E2 = s.encrypt(64, 96)
|
||||
|
||||
K2 = s.text(16, 96, 'K<tspan dy="6">2</tspan>')
|
||||
|
||||
J = s.text(128, 16, "j")
|
||||
A = s.square(128, 96, '×α <tspan dy="-8">j</tspan>')
|
||||
|
||||
P.to(X1)
|
||||
X1.to(E1)
|
||||
K1.to(E1)
|
||||
I.to(E2)
|
||||
K2.to(E2)
|
||||
E1.to(X2)
|
||||
X2.to(C)
|
||||
E2.to(A)
|
||||
J.to(A)
|
||||
s.t += arrow_path([(144,96), (160,96), (160,56), (216,56)])
|
||||
s.t += arrow_path([(144,96), (160,96), (160,144), (216,144)])
|
||||
|
||||
return SVG.format(body=s.t, title="XTS", w=372, h=192, **ARGS)
|
||||
|
||||
def save(name, data):
|
||||
f = open(f"{name}.svg", "w")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
save("ecb", ecb())
|
||||
save("ctr", ctr())
|
||||
save("cbc", cbc())
|
||||
save("xts", xts())
|
||||
192
content/blog/flash-filesystem-encryption/index.md
Normal file
192
content/blog/flash-filesystem-encryption/index.md
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
+++
|
||||
title = "Embedded filesystem encryption on flash memory"
|
||||
date = 2025-12-13
|
||||
description = "My journey in the world of filesystem encryption and flash memories."
|
||||
insert_anchor_links = "left"
|
||||
draft = true
|
||||
[taxonomies]
|
||||
tags = ["cryptography", "embedded"]
|
||||
[extra]
|
||||
katex = true
|
||||
+++
|
||||
|
||||
One of my long-term projects is an ESP32-based phone, using an SD card for storage. Then, why not encrypting the SD card?
|
||||
|
||||
_In this post, we first explain the basics of filesystem encryption, then explore ways to apply it to the case of an embedded device and flash memory. This last part is quite rarely analyzed in the litterature._
|
||||
|
||||
## Choosing a cipher
|
||||
|
||||
Cryptography is relatively expensive in computation and memory, this is why some chips include hardware implementations that are way more efficient than classical ones made of basic instructions. Fortunately, ESP32 has AES, SHA1 and SHA2 instructions, and [they are faster than software implementations](https://www.oryx-embedded.com/benchmark/espressif/crypto-esp32-s3.html). AES128 will be a good choice then.
|
||||
|
||||
If we didn't have a hardware AES, it would be interesting to look at lightweight ciphers like [ASCON](https://csrc.nist.gov/news/2023/lightweight-cryptography-nist-selects-ascon) that may be slower than AES but more memory-efficient, or more common ones such as CHACHA20 that can be faster on software.
|
||||
|
||||
## How filesystem encryption works in general
|
||||
|
||||
### Can't we just encrypt files?
|
||||
|
||||
The simpler solution would be to use a FAT filesystem encrypt files directly. But how are files encrypted, again?
|
||||
|
||||
#### ECB
|
||||
|
||||
Block ciphers like AES process data by blocks of fixed size, for instance 16 bytes. (note: AES128 has a 128-bit key and AES256 has a 256-bit key, but both process 128-bit blocks) The simplest way to encrypt data would be to divide it into blocks and encrypt each block separately.
|
||||
|
||||
<div style="text-align:center"><img alt="ECB" src="ecb.svg"/></div>
|
||||
|
||||
$$C = E(K, P)$$
|
||||
|
||||
(I love those diagrams so I made [a simple Python script](graph.py) to generate them in SVG.)
|
||||
|
||||
This mode of operation is called ECB for Electronic Code Book. It has, however, fatal flaws:
|
||||
|
||||
* If two blocks are identical, an adversary can spot them and learn information.
|
||||
* A adversary who has access to an encryption oracle (i.e. they can obtain the ciphertext from a plaintext, that could happen if you store incoming messages in the encrypted files) can try different values and decrypt other blocks by bruteforcing well-known formats.
|
||||
|
||||
ECB alone is almost never a good idea.
|
||||
|
||||
#### CTR
|
||||
|
||||
From ECB we learn that each block must be encrypted differently. Another mode that solves this problem is CTR, that uses a counter to generate a secret pseudorandom for each block. It is like generating a long one-time pad from a short key.
|
||||
|
||||
<div style="text-align:center"><img alt="CTR" src="ctr.svg"/></div>
|
||||
|
||||
$$C = P \oplus E(K, N||i)$$
|
||||
|
||||
Here, $\oplus$ is bitwise XOR and $||$ is concatenation. N is a nonce.
|
||||
|
||||
> **Could we simply vary the key using the counter, and avoid xoring?** I tried that. Every time we change AES's key, we must recompute the key expansion, which can be roughly as long as encrypting a block. Hence we save a lot when reusing the same key for many blocks.
|
||||
|
||||
CTR provides privacy, as long as the same $(K, N, i)$ is used at most once. Else, the attacker can compute $C \oplus C' = (P \oplus E(K, N||i)) \oplus (P' \oplus E(K, N||i)) = P \oplus P'$. If $P$ is only zeros (which is common in binary files), then $P'$ is fully known. Thus we have to update the nonce whenever we update the file. Storing one nonce per block would almost double the storage size! The nonce could be stored per file, but then we have to reencrypt the whole file. And what if the attacker reverted the storage into an older state, so we reuse an old nonce?
|
||||
|
||||
This mode also has a 1-bit malleability: if the attacker flips a bit, this exact bit will be flipped at decryption.
|
||||
|
||||
These two properties are fatal in our context: let's look elsewhere.
|
||||
|
||||
### Flash memories are delicate
|
||||
|
||||
#### CBC
|
||||
|
||||
CBC (Cipher Block Chaining) introduces chaining.
|
||||
|
||||
<div style="text-align:center"><img alt="CBC" src="cbc.svg"/></div>
|
||||
|
||||
$$C_0 = E(K, P_0 \oplus IV)$$
|
||||
$$C_i = E(K, P_i \oplus C_{i-1})$$
|
||||
|
||||
CBC is still somewhat malleable: if the adversary can afford scrambling one block, they can bit-flip the next one trivially.
|
||||
|
||||
CBC has another fatal property for us: modifying one block implies to re-encrypt all the following blocks.
|
||||
|
||||
The problem is that flash memories are slow and become damaged after a limited number of writes (like 10k or 100k). Rewriting entire files would take too much time and wear the device quickly.
|
||||
|
||||
#### A dedicated filesystem
|
||||
|
||||
Now that we've highlighted an important property of flash memories, it appears FAT32 may not be the best choice as a filesystem. Indeed, a modified block would stay at the same physical address, causing different regions of the storage to wear more rapidly than others. It would be better to spread the write operations across the entire space, in order to maximize the time before a failure happens.
|
||||
|
||||
[LittleFS](https://github.com/littlefs-project/littlefs) is made exactly for this purpose. Moreover, it provides atomic operations, meaning it never leaves the filesystem in an incoherent state if there is a power loss or a storage failure during a write operation.
|
||||
|
||||
If we're going down at the filesystem lever, why not going further? Instead of encrypting files, we can directly encrypt the filesystem's blocks, by placing the cryptographic module between LittleFS and the IO. LittleFS's write length can be customized so we can set it to our block length and avoid dealing with partial blocks, as we would have to do when encrypting files. Another benefit is that we're hiding the file tree as well: directories, names and metadata are encrypted as well, with no additional complexity.
|
||||
|
||||
#### XTS
|
||||
|
||||
We need something looking more like ECB or CTR in that it allows small random writes. XTS is a popular for filesystem encryption and satisfies this criterion.
|
||||
|
||||
<div style="text-align:center"><img alt="XTS" src="xts.svg"/></div>
|
||||
|
||||
$$C = E(K_1, P \oplus \Delta) \oplus \Delta$$
|
||||
$$\Delta = E(K_2, i) \times \alpha^j$$
|
||||
|
||||
Here, the storage is divided into sectors and sectors into blocks. In the diagram, i is the sector number and j is the block number.
|
||||
|
||||
Why so complicated? First, $E(K_2, i)$ looks like CTR. To make it faster, it remains constant through the entire sector (which is useful because LittleFS prefers to read or write contiguous blocks when possible). Multiplication by $\alpha$ (as defined later) is faster than a block encryption and can be computed incrementally with $x \times \alpha^j = (x \times \alpha^{j-1}) \times \alpha$. The double XOR prevents attacks on chosen ciphertext or known plaintext as described before.
|
||||
|
||||
XTS has a way to deal with final partial blocks (when data length is not a multiple of block size), but as we're encrypting full blocks of 16 bytes only, we don't need that mechanism.
|
||||
|
||||
[Rogaway 2011](https://www.cs.ucdavis.edu/~rogaway/papers/modes.pdf) criticized XTS on multiple points.
|
||||
|
||||
* XTS is based on a modified version of Rogaway's XEX mode (XOR-Encrypt-Xor) which has well understood security properties.
|
||||
* Ciphertext stealing, the way to deal with final partial blocks, is poorly designed or at least not proven secure under well-defined security goals. Again, we are not concerned.
|
||||
* The use of two different keys is unjustified, except it makes proofs easier. If the sector number i is xored with a secret random salt, there is no risk of collision between the inputs of the two cipher blocks, as long as we do not store ciphertexts of the secret key or the salt (they should be user inputs stored in volatile memory only).
|
||||
* It is a FIPS (NIST standard) but only specified in an IEEE spec that is seemingly not available publicly (unless using Sci-Hub of course).
|
||||
* $\Delta$ is byte-swapped to make implementation easier on little-endian machines, but this has no security implications.
|
||||
|
||||
## Benchmarking ciphers
|
||||
|
||||
I implemented the simplified XEX in Rust and ran a benchmark on the ESP32. As the multiplication by powers of alpha can be implemented in many ways, I also tried different versions.
|
||||
|
||||
First version, delta is an unaligned array of bytes, cast to u128 to do the maths:
|
||||
|
||||
```rust
|
||||
fn mul_delta_u128(delta: &mut [u8; 16]) {
|
||||
let mut delta1 = u128::from_be_bytes(*delta);
|
||||
delta1 = (delta1 << 1) ^ (135 * (delta1 >> 127));
|
||||
*delta = delta1.to_be_bytes();
|
||||
}
|
||||
```
|
||||
|
||||
However ESP32's registers are only 32 bits so we have to trust the compiler to implement u128 efficiently. Using [cargo-show-asm](https://crates.io/crates/cargo-show-asm), I see the above function's assembly is 151 lines long and only operates bytewise. We can do better.
|
||||
|
||||
Switching to little endian (`from_le_bytes`, `to_le_bytes`) improves quite a bit to 82 lines, but still produces byte operations only. We can still do better!
|
||||
|
||||
To ensure the compiler can work efficiently with u128, we can use u128 from the start and avoid casting, so it should be aligned:
|
||||
|
||||
```rust
|
||||
fn mul_delta_u128_aligned(delta: &mut u128) {
|
||||
*delta = (*delta << 1) ^ (135 * (*delta >> 127));
|
||||
}
|
||||
```
|
||||
|
||||
Finally we can try implementing the details ourselves:
|
||||
|
||||
```rust
|
||||
fn mul_delta_u32(delta: &mut [u32; 4]) {
|
||||
let term = (delta[3] >> 31) * 135;
|
||||
delta[3] <<= 1;
|
||||
delta[3] ^= delta[2] >> 31;
|
||||
delta[2] <<= 1;
|
||||
delta[2] ^= delta[1] >> 31;
|
||||
delta[1] <<= 1;
|
||||
delta[1] ^= delta[0] >> 31;
|
||||
delta[0] <<= 1;
|
||||
delta[0] ^= term;
|
||||
}
|
||||
```
|
||||
|
||||
The two last implementations produce 22 assembly lines, using 32 bits operations.
|
||||
|
||||
Here are the benchmark results (encrypting 100 times 128kB):
|
||||
|
||||
| Mode | Implementation | Sector size (blocks) | Time (ms) (1 key) | Time (ms) (2 keys) |
|
||||
| ---- | ----------------- | -------------------- | ----------------- | ------------------ |
|
||||
| ECB | - | - | 744 | - |
|
||||
| XTS | unaligned u128 BE | 8 | 2770 | 2774 |
|
||||
| XTS | unaligned u128 BE | 16 | 2664 | 2775 |
|
||||
| XTS | unaligned u128 BE | 32 | 2637 | 2749 |
|
||||
| XTS | unaligned u128 BE | 64 | 2624 | 2736 |
|
||||
| XTS | unaligned u128 LE | 8 | 2499 | 2448 |
|
||||
| XTS | unaligned u128 LE | 16 | 2399 | 2445 |
|
||||
| XTS | unaligned u128 LE | 32 | 2373 | 2420 |
|
||||
| XTS | unaligned u128 LE | 64 | 2361 | 2408 |
|
||||
| XTS | aligned u128 | 8 | 2549 | 2447 |
|
||||
| XTS | aligned u128 | 16 | 2449 | 2445 |
|
||||
| XTS | aligned u128 | 32 | 2424 | 2420 |
|
||||
| XTS | aligned u128 | 64 | 2412 | 2408 |
|
||||
| XTS | [u32; 4] | 8 | 2495 | 2493 |
|
||||
| XTS | [u32; 4] | 16 | 2395 | 2490 |
|
||||
| XTS | [u32; 4] | 32 | 2370 | 2465 |
|
||||
| XTS | [u32; 4] | 64 | 2357 | 2453 |
|
||||
|
||||
The fastest is XTS with one key (and salted sector number) and long sectors.
|
||||
|
||||
Sectors must not be too long, however, as random access needs computing all
|
||||
|
||||
## Storing the key
|
||||
|
||||
AES128 needs 128 bits of key, however the user will only remember ASCII words, not fully random bytes. We need something to derive a key from a variable-length password. We could just compute a hash of the password, as the ESP32 provides a hardware implementation of SHA2, but for storing passwords it is better to use a dedicated function that is fast enough to run once but hard to bruteforce efficiently on optimized systems.
|
||||
|
||||
[PBKDF2](https://fr.wikipedia.org/wiki/PBKDF2) chains thousands of calls to a hash function, each one depending on the previous one, so it is impossible to parallelize. However an attacker can run thousands of instances in parallel on a GPU or cryptocurrency-mining chip.
|
||||
|
||||
A popular choice as of today is [Argon2](https://en.wikipedia.org/wiki/Argon2), which is memory-hard: one instance requires efficient access to a big amoung of memory, potentially megabytes or even gigabytes, so it is difficult to optimize even on dedicated hardware. Problems are that its implementation is quite complicated (it will take too much ROM) and its specs are not even complete.
|
||||
|
||||
[Catena](https://www.researchgate.net/publication/261548591_The_Catena_Password_Scrambler) is a scheme with similar properties but with a very simple description. It takes less than 50 lines of Rust. To run on the ESP32, I used SHA256 and set its memory usage to 128kB and 1024 iterations. In comparison, recommended parameters are between 67MB and 1GB with 3 or 4 iterations. It runs in 911ms. We can expect a speedup of more than 10 on a good CPU, and it still can be parallelized easily on an old GPU: if your GPU has 1GB of RAM, it can hold at most 8192 parallel instances.
|
||||
|
||||
The benefit of password hashing functions on the ESP32 is a bit disappointing, we only slow down attacks by a small factor. It seems easier to enforce strong passwords. Picking 10 random words from a [BIP39](https://github.com/bitcoin/bips/blob/04b448b599cb16beae40ba9a98df9f262da522f7/bip-0039/english.txt) wordlist gives $\log_2(2048^{10})=110$ bits of entropy. To make it faster to type, each word can be shortened to its 4 first letters without loosing entropy.
|
||||
77
content/blog/flash-filesystem-encryption/xts.svg
Normal file
77
content/blog/flash-filesystem-encryption/xts.svg
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="372" height="192" viewBox="0 0 372 192">
|
||||
<!-- Generated by https://txmn.tk/blog/flash-filesystem-encryption/graph.py -->
|
||||
<!-- Image released under license CC0 (public domain) -->
|
||||
<title>XTS</title>
|
||||
<style>
|
||||
text.t {
|
||||
color: #000;
|
||||
fill: #000;
|
||||
text-anchor: middle;
|
||||
dominant-baseline: central;
|
||||
font-size: 16px;
|
||||
font-family: "Libertinus Sans";
|
||||
}
|
||||
circle, line, rect, path.s {
|
||||
stroke: #000;
|
||||
stroke-width: 2px;
|
||||
fill: none;
|
||||
}
|
||||
path.f {
|
||||
stroke: none;
|
||||
fill: #000;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
text.t {
|
||||
color: #ddd;
|
||||
fill: #ddd;
|
||||
}
|
||||
circle, line, rect, path.s {
|
||||
stroke: #ddd;
|
||||
}
|
||||
path.f {
|
||||
fill: #ddd;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<text class="t" x="224" y="16">P</text>
|
||||
<circle r="8" cx="224" cy="56"/>
|
||||
<line x1="216" y1="56" x2="232" y2="56"/>
|
||||
<line x1="224" y1="48" x2="224" y2="64"/>
|
||||
<rect x="208" y="80" width="32" height="32"/>
|
||||
<text class="t" x="224" y="96">E</text>
|
||||
<circle r="8" cx="224" cy="144"/>
|
||||
<line x1="216" y1="144" x2="232" y2="144"/>
|
||||
<line x1="224" y1="136" x2="224" y2="152"/>
|
||||
<text class="t" x="224" y="180">C</text>
|
||||
<text class="t" x="176" y="96">K<tspan dy="6">1</tspan></text>
|
||||
<text class="t" x="64" y="16">i</text>
|
||||
<rect x="48" y="80" width="32" height="32"/>
|
||||
<text class="t" x="64" y="96">E</text>
|
||||
<text class="t" x="16" y="96">K<tspan dy="6">2</tspan></text>
|
||||
<text class="t" x="128" y="16">j</text>
|
||||
<rect x="112" y="80" width="32" height="32"/>
|
||||
<text class="t" x="128" y="96">×α <tspan dy="-8">j</tspan></text>
|
||||
<line x1="224" y1="28" x2="224.0" y2="40.0"/>
|
||||
<path class="f" transform="translate(224,48) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="224" y1="64" x2="224.0" y2="72.0"/>
|
||||
<path class="f" transform="translate(224,80) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="188" y1="96" x2="200.0" y2="96.0"/>
|
||||
<path class="f" transform="translate(208,96) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="64" y1="28" x2="64.0" y2="72.0"/>
|
||||
<path class="f" transform="translate(64,80) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="28" y1="96" x2="40.0" y2="96.0"/>
|
||||
<path class="f" transform="translate(48,96) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="224" y1="112" x2="224.0" y2="128.0"/>
|
||||
<path class="f" transform="translate(224,136) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="224" y1="152" x2="224.0" y2="160.0"/>
|
||||
<path class="f" transform="translate(224,168) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="80" y1="96" x2="104.0" y2="96.0"/>
|
||||
<path class="f" transform="translate(112,96) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<line x1="128" y1="28" x2="128.0" y2="72.0"/>
|
||||
<path class="f" transform="translate(128,80) rotate(90.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<path class="s" d="M144 96L160 96L160 56L208.0 56.0"/>
|
||||
<path class="f" transform="translate(216,56) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
<path class="s" d="M144 96L160 96L160 144L208.0 144.0"/>
|
||||
<path class="f" transform="translate(216,144) rotate(0.0)" d="M0 0l-8 0l-4 6l12 -6l-12 -6l4 6z"/>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
|
|
@ -26,14 +26,17 @@ JavaScript is obviously not made for being used in webpages.
|
|||
* no native basic templating system (need `String.replaceAll("{{foo}}", foo)`)
|
||||
* no easy-to-use bytes array (like `Bytes` in Python or `Vec<u8>` in Rust), but many different bytes arrays with weird interfaces
|
||||
* no native cryptographic primitives if you aren't using HTTPS and satisfying other strange "security" requirements (you often end up inefficiently implementing SHA2 or AES in JS, while the browser natively uses OpenSSL)
|
||||
* multithreading ("web worker" because it's web we need fancy names) requires a separate source file, hence an additional network request
|
||||
* no native date/time string formatting
|
||||
* When defining an anonymous function (such as a callback) you can't reliably move variables to it.
|
||||
|
||||
Having a decent standard library would reduce the use of weird hacks hence the number of bugs, and the use of libraries hence the traffic and webpage size and loading time.
|
||||
Compare it to Python, which has a good set of standard types, easy to use and equipped with most of expected methods (even if some obvious ones are missing, like `signum`).
|
||||
Knowing all that, I couldn't stand hearing Rust's standard library is too light.
|
||||
|
||||
## What the fuck
|
||||
|
||||
JavaScript doesn't respect anything.
|
||||
JavaScript respects nothing.
|
||||
|
||||
* You never know whether you're copying or borrowing (a table or an object), and how deeply.
|
||||
* Python suffers the same problem.
|
||||
|
|
|
|||
35
content/blog/lrs-antispam.md
Normal file
35
content/blog/lrs-antispam.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
+++
|
||||
title = "Can antibots be both efficient and anonymous?"
|
||||
date = 2025-09-20
|
||||
description = "todo"
|
||||
insert_anchor_links = "left"
|
||||
draft = true
|
||||
[taxonomies]
|
||||
tags = ["cryptography"]
|
||||
[extra]
|
||||
katex = true
|
||||
+++
|
||||
|
||||
Some people with a lot of money (or, at least, who control a lot of machines) decided to flood the Internet with useless requests, crawling every website without respecting robots.txt nor anything else. They even made some services shut down, effectively performing a DDoS. All that to train AI with garbage data so you can ask a chatbot to quote Wikipedia or StackOverflow instead of using a proper search engine.
|
||||
|
||||
The Internet relies on a heap of protocols that only work well when people behave correctly: it stops being efficient when someone gains too much power (bandwidth and IP addresses). Cloud providers indeed provide bad guys with enough clouds to make a storm, not to talk about the "Internet of Things" that allows botnets to run on security cameras, baby monitors and sextoys. One of the most common practices on the Internet is fundamentally altruistic: giving a copy of a file to whomever is asking for it, for free (what it commonly called "Web"). The problem is that answering such a request consumes a machine's resources (energy, computing time, IO time, memory, etc.), resources that can be exhausted if people are asking too much.
|
||||
|
||||
## A few solutions
|
||||
|
||||
### Proof of intelligence
|
||||
|
||||
Captchas ask the user to solve a problem that is (supposedly) difficult for a computer, but (supposedly) easy for a human. However they take time to solve even for a human, they are not accessible to people who can't see or hear or have mental disabilities, and modern AIs can already solve them.
|
||||
|
||||
### Proof of browser
|
||||
|
||||
Systems that do not require user input can check whether they are being run in a proper web browser, by testing various features. However they can be fooled by giving more power to the bot's engine, which then becomes indistinguishable from a browser.
|
||||
|
||||
### Proof of work
|
||||
|
||||
Proof of work imposes to solve problems that are long to solve, but fast to check. A few seconds of computing time is needed to solve the challenge. The difficulty must be well balanced so it is fast enough for a legitimate user, but too expensive for a spammer who sends thousands of requests per second. However this appears not to frighten spammers anymore, as Anubis (an antispam system based on proof of work) failed to stop some attacks.
|
||||
|
||||
### Global monitoring
|
||||
|
||||
If you are big enough to have a global database of real time traffic per IP address (e.g. CloudFlare, Amazon, Google, etc.), you can detect spammy addresses and stop them immediately. However such a centralized solution is not acceptable, as it gives too much power to gigantic corporations. Decentralized and anonymous spam databases may be an interesting research subject but it seems quite complicated.
|
||||
|
||||
##
|
||||
|
|
@ -1,14 +1,15 @@
|
|||
+++
|
||||
title = "Conseils de lectures"
|
||||
date = 2022-09-24
|
||||
description = "Livres et articles que vous devriez lire."
|
||||
date = 2025-12-21
|
||||
description = "Livres et articles que je recommande."
|
||||
insert_anchor_links = "left"
|
||||
draft = true
|
||||
[taxonomies]
|
||||
tags = ["lecture"]
|
||||
+++
|
||||
|
||||
Les titres marqués d'une étoile ✪ sont à lire absolument !
|
||||
Les titres marqués d'une étoile ✪ sont ceux que je recommande le plus fortement !
|
||||
|
||||
> En France, tous les livres (ou presque) peuvent être commandés à prix unique dans toutes les librairies, et il y a des librairies indépendantes quasiment partout. Il n'y a pas d'excuse pour en acheter sur Amaz0n !
|
||||
|
||||
## Politique {#politique}
|
||||
|
||||
|
|
@ -28,24 +29,39 @@ Inscrire le capitalisme dans une histoire plus large.
|
|||
|
||||
### ✪ Bullshit jobs
|
||||
|
||||
Essai de David Graeber, 2018
|
||||
Essai de David Graeber, 2018. Enquête anthropologique sur les boulots à la con, qui représenteraient une part non négligeable du temps de travail de la société. Des boulots inutiles ou rendus inutiles par une organisation du travail inefficace, un management hors-sol, le marché "efficient".
|
||||
|
||||
Lecture qui ne peut que soutenir l'hypothèse qu'une gestion collective du travail nous amènerait probablement à travailler moins, et mieux.
|
||||
|
||||
### ✪ 1984
|
||||
|
||||
Roman de George Orwell, 1949
|
||||
|
||||
Classique mais trop souvent considéré comme une fiction.
|
||||
|
||||
[Cette vidéo de Monsieur Phi](https://invidious.fdn.fr/watch?v=LbaLhI2WgpY) peut éclairer la lecture et approfondir l'interprétation simpliste qu'on fait souvent de ce livre.
|
||||
[Cette vidéo de Monsieur Phi](https://redirect.invidious.io/watch?v=LbaLhI2WgpY) peut éclairer la lecture et approfondir l'interprétation simpliste qu'on fait souvent de ce livre.
|
||||
|
||||
### ✪ Un monde sans copyright... et sans monopole
|
||||
|
||||
Essai de Joost Smiers et Marieke van Schijndel, 2011, chez Framabook. [fiche de l'éditeur](https://archives.framabook.org/un-monde-sans-copyright-et-sans-monopole-2/index.html)
|
||||
|
||||
Histoire du droit d'auteur, démontage de l'argumentaire selon lequel il permettrait de protéger les artistes (spoiler, il participe plutôt à l'uniformisation de la culture et à son sontrôle par des monopoles), et alternative.
|
||||
|
||||
### ✪ Les dépossédés
|
||||
|
||||
Roman d'Ursula Le Guin, 1974
|
||||
|
||||
Une utopie libertaire (anarchiste et communiste) qui, plutôt que d'étaler béatement une société au fonctionnement naïf et parfait, raconte la difficulté de vivre dans un monde aride mais social, avec le style anthropologie-fiction du cycle de l'Ekumen.
|
||||
|
||||
### Karl Marx, le retour
|
||||
|
||||
### Le gang de la clé à molette
|
||||
Courte pièce d'Howard Zinn
|
||||
|
||||
Marx revient d'entre les morts, et il est bien consterné par ce qu'on appelle le marxisme depuis l'URSS... Un rappel que le communisme n'a rien à voir avec Staline et Mao.
|
||||
|
||||
### Zendegi
|
||||
|
||||
Roman de Greg Egan.
|
||||
|
||||
Réalité virtuelle, expériences de scan cérébral pour milliardaires et pour PNJ de jeux vidéo, révolution iranienne, manifestants organisés avec des téléphones en réseaux maillés et infrarouge pour déjouer la coupure des points d'accès...
|
||||
|
||||
## Esprit critique
|
||||
|
||||
|
|
@ -83,16 +99,18 @@ Que se passe-t-il quand vous avez perdu toute votre personnalité, et pouvez cho
|
|||
|
||||
### Trois hommes dans un bateau
|
||||
|
||||
Roman
|
||||
Roman de Jerome K. Jerome
|
||||
|
||||
Comique, plein d'ironie, de double-standard, de mauvaise foi des personnages.
|
||||
|
||||
### L'Œuvre au Noir
|
||||
|
||||
### Le cycle de l'Ekumen
|
||||
Roman de Marguerite Yourcenar
|
||||
|
||||
La vie d'un clerc dans une Europe médiévale, qui en travaillant en sciences et en médecine, devient sceptique vis-à-vis du dogme religieux, jusqu'à s'attirer des ennuis avec l'Inquisition.
|
||||
|
||||
## Mathématiques
|
||||
|
||||
### Logicomix
|
||||
|
||||
BD. L'histoire de Bertrand Russel et de son travail sur la formalisation du raisonnement mathématique et des fondations de la logique et de l'arithmétique. Accessible même si on est nul en maths.
|
||||
|
|
|
|||
51
content/blog/torrent-guide/index.fr.md
Normal file
51
content/blog/torrent-guide/index.fr.md
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
+++
|
||||
title = "[Tuto] Télécharger des films sans risque"
|
||||
date = 2025-11-22
|
||||
description = "Guide facile pour se procurer des films sans danger"
|
||||
insert_anchor_links = "left"
|
||||
draft = true
|
||||
[taxonomies]
|
||||
tags = []
|
||||
+++
|
||||
|
||||
Ce guide explique comment obtenir des films en essayant de ne pas prendre de risque (juridique ou informatique).
|
||||
|
||||
## BitTorrent
|
||||
|
||||
On utilisera BitTorrent, un système d'échange de fichiers en pair-à-pair, qui permet à tout le monde de partager des fichiers. C'est beau, c'est libre, c'est gratuit !
|
||||
|
||||
Un _torrent_ est l'identifiant unique un fichier ou ensemble de fichiers, tel un code-barres infalsifiable. Une fois qu'on a récupéré un torrent, que ce soit sous forme de fichier `.torrent` ou de lien `magnet`, on peut demander au logiciel de chercher des gens qui possèdent une copie des fichiers correspondants, et de les télécharger par petits bouts. En même temps qu'on télécharge, on envoie les petits bouts déjà obtenus à d'autres gens qui demandent à le télécharger. Ainsi l'efficacité et la résilience du réseau repose sur la réciprocité.
|
||||
|
||||
## Légalité et financement
|
||||
|
||||
Attention, la plupart des films sont soumis à la propriété intellectuelle et appartiennent aux pires capitalistes qui soient.
|
||||
|
||||
Ne pas payer un film porte préjudice aux actionnaires de Disney, Universal, Sony, Amazon, Netflix et autres joyeux philanthropes. Ne faites pas ça.
|
||||
|
||||
Différents pays soufrent de législations rendant plus ou moins risqué le téléchargement. La liste ci-dessous sera complétée ultérieurement.
|
||||
* **France** : L'HADOPI est censée interdire le téléchargement illicite. Cependant, trois lettres d'avertissement précèdent l'amende. Il n'y a aucun risque de sanction avant réception d'un avertissement.
|
||||
* **Allemagne** : Il semblerait que le risque d'amende et de coupure de réseau soit avéré. [(source)](https://www.comparitech.com/blog/vpn-privacy/how-safe-is-torrenting-in-germany/)
|
||||
|
||||
## Où trouver des torrents
|
||||
|
||||
Il existe des sites spécialisés dans l'indexation de torrents. En voici quelques uns qui fonctionnent actuellement (j'essaierai de mettre à jour la liste si besoin) :
|
||||
* [YTS](https://yts.mx) : pratique, montre les infos sur les films, fiable, avec des commentaires plus ou moins pertinents, et des sous-titres.
|
||||
* [1337x](https://1337x.to) : pratique, montre les infos sur les films, liste aussi d'autres torrents dont des jeux-vidéo.
|
||||
* [torrents.csv](https://torrents-csv.com) : austère, ne montre pas d'infos sur les torrents.
|
||||
|
||||
### Gare aux virus
|
||||
|
||||
Il est assez rare de tomber sur des programmes malveillants, puisque les sites intègrent des systèmes de notation, de signalement et que les pairs éviteront les partager. Mais ça peut arriver, donc voici quelques recommendations :
|
||||
* Avoir un système à jour. Vos système d'exploitation, navigateur web et lecteur de vidéo doivent être mis à jour assez souvent pour ne pas être sujets à de vieilles failles de sécurité.
|
||||
* Lire les commentaires du torrent s'il y en a, pour vérifier si quelqu'un avertit d'un danger.
|
||||
* Vérifier les extensions de fichiers du torrent sur le site ou dans votre logiciel de torrent. Le fichier du film doit être indiqué comme un fichier vidéo et non comme un programme exécutable ou un script dans le gestionnaire de fichiers. Si une fenêtre du système vous demande si vous voulez l'exécuter, alors dites non !
|
||||
* Si votre système est plus moderne que Windows XP, le risque d'attraper un virus juste en téléchargeant un fichier _sans l'ouvrir_ est très faible.
|
||||
* Si vous voulez prendre plus de précautions, créer un autre compte utilisateur qui sera dédié aux torrents et qui n'aura pas les droits d'administration. Un éventuel programme malveillant aura plus de mal à contaminer votre compte principal.
|
||||
|
||||
### Si les sites ne sont pas accessibles
|
||||
|
||||
Il est possible que vous ne puissiez pas accéder à certains sites de torrents. Il faut savoir qu'en France, le blocage partiel d'Internet est interdit. Le blocage passe en fait par les serveurs DNS, qui servent à obtenir l'adresse IP d'un serveur à partir de son nom de domaine. Pour lever le blocage (ce qui est tout à fait légal), il suffit de changer de DNS, je vous laisse chercher comment faire sur votre système ou dans votre navigateur. Vous pouvez utiliser les [résolveurs de la FDN](https://www.fdn.fr/actions/dns/).
|
||||
|
||||
## Télécharger
|
||||
|
||||
https://transmissionbt.com/
|
||||
|
|
@ -83,7 +83,7 @@ main a:hover, main a:focus, main a:visited:hover, main a:visited:focus {
|
|||
position: sticky;
|
||||
top: 16px;
|
||||
float: left;
|
||||
max-width: calc(50vw - 444px);
|
||||
max-width: calc(50vw - 450px);
|
||||
background-color: #dddc;
|
||||
padding: 16px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,14 +46,13 @@
|
|||
<a href="https://demo.cesium.app/#/app/wot/45GfjkWCWQhJ3epJVGC2NSg1Rcu4Ue1vDD3kk9eLs5TQ/"><img alt="Libre currency Ğ1" src="/img/gbreve-white.svg" style="height:32px"/></a>
|
||||
</div>
|
||||
<p>
|
||||
Electronic missive:<br/>
|
||||
tuxmain ât zettascript ðøt org
|
||||
{{ tr.footer_mail | safe }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="footer-pane">
|
||||
<p>
|
||||
Hosted in Bordeaux, France.<br/>
|
||||
🦀 Site generated with <a href="https://www.getzola.org/">Zola</a>.<br/>
|
||||
{{ tr.footer_hosted }}<br/>
|
||||
🦀 {{ tr.footer_generated }} <a href="https://www.getzola.org/">Zola</a>.<br/>
|
||||
<a href="https://git.txmn.tk/tuxmain/website">Source code</a> under <a rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue