Compare commits

...
Sign in to create a new pull request.

63 commits
tmp ... main

Author SHA1 Message Date
jaseg
0d689a700c Publish git migration note and pre-write project announcements 2026-05-30 11:44:14 +02:00
jaseg
fb34ff0c64 Add pixacao project blog post 2026-05-30 10:30:31 +02:00
jaseg
12db898381 Fix kicoil blog post formatting 2026-05-30 10:29:42 +02:00
jaseg
0e09cd96ed Add new deploy script 2026-05-17 13:46:09 +02:00
jaseg
00005da741 projects/kicoil: fix issue tracker URL 2026-04-24 21:57:21 +02:00
jaseg
ea0940ff66 Add kicoil project page 2025-12-29 12:04:20 +01:00
jaseg
d8bee2acc2 Properly link mastodon profile 2025-12-08 00:06:52 +01:00
jaseg
ccdffd6c65 Remove projects list from home page 2025-11-18 11:51:06 +01:00
jaseg
904f21f1e9 Add cgit pdf download post 2025-11-18 11:49:48 +01:00
jaseg
fed2333433 Sampling mesh monitor post: Fix summary 2025-10-27 12:36:51 +01:00
jaseg
4993431bbe blog/sampling mesh mon: add git link 2025-10-21 22:10:17 +02:00
jaseg
7fefec320d sampling mesh monitor post: add pics 2025-10-21 11:33:28 +02:00
jaseg
ebb0df945e Add sampling mesh monitor paper 2025-10-21 11:12:25 +02:00
jaseg
0a095fa359 theme: fix page background and make headings slightly smaller 2025-10-21 10:59:33 +02:00
jaseg
d70e23068c kimesh: fix link in readme 2025-09-28 22:13:57 +02:00
jaseg
d2891b448a Add wsdiff article 2025-07-28 14:08:30 +02:00
jaseg
3fd0c107e0 css code listings post: Fix code link 2025-07-26 17:53:13 +02:00
jaseg
02abb01549 code listing post: Fix code listing, remove unnecessary part 2025-07-26 16:15:51 +02:00
jaseg
90e8ff7ac0 Fix page layout: wrap long literals 2025-07-26 16:10:55 +02:00
jaseg
f5d03ed1cf Add code listing CSS hack article 2025-07-26 16:03:33 +02:00
jaseg
338de75fb4 Fix font preloads, add speculative prefetch rules 2025-07-26 14:24:12 +02:00
jaseg
9d55ae84a8 Improve performance by preloading webfonts 2025-07-26 13:45:58 +02:00
jaseg
322eddf574 More rendering fixes 2025-06-30 15:40:28 +02:00
jaseg
752f296a77 Rendering fixes 2025-06-30 15:39:45 +02:00
jaseg
16655c00e0 Fix hugo layout in hugo > ~0.130.0
For some reason, newer hugo versions have trouble with .summary, and on
some pages where .summary is not defined in the page header metadata,
when rendering the page as a preview card, hugo swallows the card's
closing </div> tag. Such a weird bug, we now just work around it by
explicitly setting the .summary meta on pages that cause this bug(?) to
surface.
2025-06-30 15:35:25 +02:00
jaseg
a324ba7b64 WIP 2025-06-30 14:48:34 +02:00
jaseg
bce789de7b epa-sgd-crypt draft 2025-01-05 15:11:58 +01:00
jaseg
9ee28abd50 deploy.py: Add pagefind build 2023-12-30 16:37:50 +01:00
jaseg
74e119b6e2 theme: Add static page search with pagefind 2023-12-30 16:35:36 +01:00
jaseg
b357b50525 projects/8seg: Update for 37C3 teardown. 2023-12-30 13:43:43 +01:00
jaseg
c12db2ab42 projects/8seg: add 3D model link 2023-12-28 14:55:59 +01:00
jaseg
2bb3b936b4 Add 8seg project page and blog post 2023-12-26 15:30:15 +01:00
jaseg
77fffce8a2 update 8seg tech overview 2023-12-21 19:39:19 +01:00
jaseg
d7ca6c9cdc 8seg post WIP 2023-12-21 19:37:35 +01:00
jaseg
29f6e652de Make package signing key not mess up the layout on /about 2023-10-14 17:43:31 +02:00
jaseg
5fb6e37fdc Fix mastodon link 2023-10-14 17:41:38 +02:00
jaseg
588b0a9e10 theme: Make things degrade gracefully on mobile 2023-10-14 17:40:05 +02:00
jaseg
096ffb8c4e Size down background image 2023-10-14 14:55:42 +02:00
jaseg
f87afaaf44 Make background really pretty 2023-10-14 14:54:07 +02:00
jaseg
2cdeee2a3c Nyght Serif is the best :) 2023-10-14 13:02:49 +02:00
jaseg
dff5da4d50 Add google web environment integrity protest message 2023-10-14 12:08:40 +02:00
jaseg
2fd22e30ce template: Make projects sort by date 2023-10-06 14:35:32 +02:00
jaseg
9afd55a603 Projects: Add kimesh 2023-10-06 14:14:06 +02:00
jaseg
32d3a98101 Update theme 2023-09-27 13:05:38 +02:00
jaseg
81f65823f2 Add missing gerbolyze screenshots 2023-03-19 23:43:37 +01:00
jaseg
aa54c194ff Make images on dark mode darker 2023-03-19 23:41:06 +01:00
jaseg
9d2ecb11fd Make light mode prettier 2023-03-19 23:24:27 +01:00
jaseg
6658952dcf dark mode 2023-03-19 23:02:57 +01:00
jaseg
3c6957467f Update project link display 2023-03-19 18:57:48 +01:00
jaseg
c55e92fe93 Fix headline alignment & deployment 2023-03-19 15:55:56 +01:00
jaseg
77469be23f Make site mobile-friendly, make code listings pretty 2023-03-19 15:27:45 +01:00
jaseg
92e3b5f49f Big site update 2023-03-19 00:53:31 +01:00
jaseg
072b2d38e2 Add Telekom GPON SFP ONU page 2022-02-23 22:34:05 +01:00
jaseg
5e93f56038 Update robots.txt, allow crawling 2022-02-21 12:19:50 +01:00
jaseg
fdb87d6c0b serial protocol post: light proofreading, fix link 2021-11-25 12:40:49 +01:00
jaseg
e7a91af6b5 Update theme to show post list on top level again 2021-11-25 11:54:39 +01:00
jaseg
396c62e422 Polish wifi LED driver post 2021-11-24 12:31:49 +01:00
jaseg
a6ecebf648 Add IHSM post 2021-11-24 12:27:03 +01:00
jaseg
c4af22d852 Finish HSM basics post 2021-11-23 18:40:11 +01:00
jaseg
3dfca328ee deploy.py: use absolute URL instead of remote name for deployment 2021-08-16 14:28:04 +02:00
jaseg
f61c032d23 deploy.py: auto-push generated commit 2021-08-16 14:27:08 +02:00
jaseg
345867359e about page: fix mailto link 2021-08-16 14:26:16 +02:00
jaseg
28dae21621 Fill in about page 2021-08-16 14:23:40 +02:00
357 changed files with 17347 additions and 1028 deletions

View file

@ -1,4 +1,76 @@
baseURL = "https://blog.jaseg.de/"
baseURL = "http://jaseg.de/"
languageCode = "en-us"
title = "jaseg.de"
theme = "hugo-classic"
title = "Home"
copyright = "Jan Sebastian Götte"
theme = "conspiracy"
enableRobotsTXT = true
[outputs]
home = ['html', 'rss']
taxonomy = ['html', 'rss']
[params]
fediverse_account = "@jaseg@chaos.social"
[taxonomies]
category = "Categories"
blog = "Posts"
[[menu.main]]
name = "Home"
url = "/"
weight = 1
[[menu.main]]
name = "Blog"
url = "/blog/"
weight = 2
[[menu.main]]
name = "Projects"
url = "/projects/"
weight = 3
[[menu.main]]
name = "About"
url = "/about/"
weight = 4
[[params.profile_links]]
name = "cgit"
url = "https://git.jaseg.de/"
weight = 1
[[params.profile_links]]
name = "Github"
url = "https://github.com/jaseg"
weight = 2
[[params.profile_links]]
name = "Gitlab"
url = "https://gitlab.com/neinseg"
weight = 3
[[params.profile_links]]
name = "Mastodon"
url = "https://chaos.social/@jaseg"
weight = 4
[[params.footer_links]]
name = "About"
url = "/about/"
weight = 1
[[params.footer_links]]
name = "Imprint"
url = "/imprint/"
weight = 2
[[params.homepage_categories]]
title = "Blog"
key = "blog"
weight = 2
count = 10
[security.exec]
allow = ["^dart-sass-embedded$", "^go$", "^npx$", "^postcss$", "^rst2html$"]

9
content/_index.rst Normal file
View file

@ -0,0 +1,9 @@
---
title: jaseg.de
---
Hi there, and welcome to my personal website.
I'm jaseg, and I write about my projects here. You can find long-form articles in the blog, and links to my open-source
projects on the projects page. On the top right of this page, there are links to my git repositories and social media
pages. If you want to learn more about me, head over to the about page.

View file

@ -1,7 +0,0 @@
---
title: "About jaseg"
---
About
-----

65
content/about/index.rst Normal file
View file

@ -0,0 +1,65 @@
---
title: "About jaseg"
---
About
-----
Hej, I'm Jan, or jaseg. At the moment I'm doing a PhD (Dr.-Ing.) at TU Darmstadt in Computer Science, specializing on
Hardware Security. This is my personal website where I publish things that I find interesting.
I self-host my code at `git.jaseg.de <https://git.jaseg.de/>`__, but I am also on `github <https://github.com/jaseg>`__
and on `gitlab <https://gitlab.com/neinseg>`__. I use github for issue tracking for some of my projects such as
`gerbolyze <https://github.com/jaseg/gerbolyze>`__ and `python-mpv <https://github.com/jaseg/python-mpv>`__. I maintain
the `python-mpv <https://pypi.org/project/python-mpv/>`__ and `gerbolyze <https://pypi.org/project/gerbolyze/>`__ python
packages on PyPI. Release tags on these two repositories are signed with the release signing key found `on github
<https://github.com/jaseg.gpg>`__ and below.
I am not on any social network, but feel free to write me an email at `hello@jaseg.de
<mailto:hello@jaseg.de?subject=About\ page\ on\ blog.jaseg.de>`__.
I do not use application-level email encryption such as S/MIME or PGP. If you need a higher level of secrecy than
regular old email provides, please ask around for my signal contact or email me a file encrypted using `age
<https://github.com/FiloSottile/age>`__ with one of the SSH keys listed `on my github
<https://github.com/jaseg.keys>`__. You can find both PGP and other SSH keys that I have used in the past on the
internet, but please consider these keys revoked, and do not use them to encrypt anything you send me.
Python package release signing key
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I use this GPG key (key ID ``ED7A208EEEC76F2D``) to sign git release tags of both `gerbolyze <https://github.com/jaseg/gerbolyze>`__ and `python-mpv
<https://github.com/jaseg/python-mpv>`__:
.. code::
-----BEGIN PGP PUBLIC KEY BLOCK-----
mDMEXom49xYJKwYBBAHaRw8BAQdA/KrWMt2MKGIZUvlQZnWjNd6i8/ZYjRsBQqEf
PJ8pJ+20NHB5dGhvbi1tcHYgUmVsZWFzZSBTaWduaW5nIEtleSA8cHl0aG9uLW1w
dkBqYXNlZy5kZT6IlgQTFggAPhYhBONvdTB/Cg7C0UX/XO16II7ux28tBQJeibj3
AhsDBQkSzAMABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEO16II7ux28thRYA
/3Yl1RdeUGor6K0RTxce9TIBB+DpLNupJgB9f6onuocpAQC614zQ/RQ6rkGTHCwA
ElFClWRQ5eppj0jpAuH15udqAbg4BF6JuPcSCisGAQQBl1UBBQEBB0A0mrXSv6rj
ajCmZR4H4OtowAx477YS+yWARqo1NtdgJwMBCAeIfgQYFggAJhYhBONvdTB/Cg7C
0UX/XO16II7ux28tBQJeibj3AhsMBQkSzAMAAAoJEO16II7ux28tMZwBAIUpHHvP
gRW2jQuzdw1r06kItfFk/0t+mgNUQ2+vtbhzAP98BoWx7lv+bvlIbBaVgLldusj0
pHnZI/0y3ksMBkdbBw==
=Mr6G
-----END PGP PUBLIC KEY BLOCK-----
About this site
---------------
This site is made with the hugo static site generator. I made the theme myself, feel free to grab a copy at
`git.jaseg.de <https://git.jaseg.de/blog.git/tree/themes/conspiracy?h=main>`__. The nifty auto-reflowing code embeds are
made with some CSS magic I made that you can find in `style.css
<https://git.jaseg.de/blog.git/tree/themes/conspiracy/assets/css/style.css?h=main&id=2fd22e30ce176d8d8a641fd371ad1623b082eaaf#n367>`__.
The body text is typeset in Roboto Slab, created by `Christian Robertson <https://christianrobertson.com/>`__ while
working at Google. The headlines are set in Nyght Serif, a font by `Maksym Kobuzan <https://linktr.ee/mkobuzan>`__.
Check out their other fonts, their work is beautiful! Source code is typeset in Fira Code, a derivate by ... from
Mozilla's `Fira Mono <https://github.com/mozilla/Fira>`__ font, designed by `Erik Spiekermann
<https://spiekermann.com/>`__, `Ralph du Carrois <https://carrois.com/>`__, `Anja Meiners
<https://anjameiners.com/de/hallo/>`__ and Botio Nikoltchev of Carrois Type Design, now succeeded by `bBoxType
<https://bboxtype.com/typefaces/FiraMono/#!layout=specimen>`__ , and Patryk Adamczyk of Mozilla. The photo of mountains
that's used in the background of this site is by `Fabrizio Conti <https://www.conti.photos/>`__ and can be found on
`Unsplash <https://unsplash.com/photos/TUmjK7ZJgbI>`__.

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

203
content/blog/8seg/index.rst Normal file
View file

@ -0,0 +1,203 @@
---
title: "8seg Technical Overview"
date: 2023-12-26T15:26:00+01:00
summary: >
8seg is a large-scale LED light art installation that displays text on a 1.5 meter high, 30 meter wide
8-segment display made from cheap LED tape.
---
Prologue
--------
German hacker culture has this intense love for things that light up in colorful ways. Like for many others in this
community, I have always been fascinated by LEDs. One of the first things on my pile of unfinished projects was to build
my own LED matrix and use it to display text. When I started that project, I was still new to electronics. Back then,
commercial LED matrices were limited to red or green color only, and were very expensive, so there was an incentive to
build your own. At the same time, while individual LEDs were'nt expensive anymore, they hadn't started to be cheap yet,
either. On top of the material cost, back then there were no PCB fabs, and especially no PCB assembly houses that a
hobbyist could afford. Ultimately, I ended up never finishing this project because I felt it was more of a feat of
material wealth than of technical prowess.
Over time, LEDs came down in price, and peoople started using them in all sorts of fun things. Around the mid-2010s,
cheap-ish, ready-made tapes and chains of RGB LEDs that included WS2811 or similar digitally controllable driver chips
led to a cambrian explosion in projects involving large amounds of colorful LEDs since suddenly, all you needed was an
arduino and a beefy power supply to individually control an almost unlimited number of these LEDs.
Today, LED technology has advanced even furhter, to a point where now you can buy staggering quantities of the second
generation of these controllable LEDs that provides better color rendering embedded in all sorts of shapes, from tapes
through rings to grids. When I built the first matelight_ in 2013, the matelight's 640 individually-controllable LEDs
were *a lot*. Today, you can buy a roll with several thousand channels for about the price of a nice pizza.
The idea behind 8seg
--------------------
Living through this amazing escalation of LED technology, in 2018, I looked at a then-obsolete piece of single-color,
dumb, non-controllable LED tape with a simple question in mind: Taking this unsophisticated artifact of yesterday's
technology, what would be the coolest thing I could build from it? Can I buld something that not only rivals, but
outmatches the modern controllable LED stuff? From that question, I set myself two goals. First, I wanted to keep the
project's use of financial and labor resources reasonable. A lot of art consists of taking a simple idea, and simply
extrapolating its implementation to a ridiculous scale at the expense of the artist's time and wallet. That wasn't the
point I wanted to make. I wanted to make something cool from an obsolete technology, not prove how much patience I had
soldering. My second goal was to create something that is meaningfully controllable. Controllability is much harder with
these dumb LED tapes, but it is possible nontheless, and I wanted to test out how far you could go with it.
After thinking through a number of possibilities, I settled on the basics of the 8seg design I ended up realizing. The
installation would be a banner-style display consisting of a series of characters made from non-controllable LED tape.
The banner can be rigged up in any convenient air space, bending and folding to conform to the space's shape and size.
The key idea behind 8seg is that it makes up for it's lack of control fidelity with sheer size. If nothing else, this
non-controllable LED tape is *cheap*.
The design of a single 8seg character
-------------------------------------
Each 8seg character consists of 8 *segments* of LED tape that are inter-connected through small circuit boards, four in
the corners, and one in the center. As it turns out, 8 segments arranged in this shape are enough to display all of the
English language's alphabet as well as numbers in a weird, but readable form.
The electrical design of an 8seg character has one weird trick at its core. To avoid having to run a bunch of wires from
some kind of driver circuit board to each of the eight segments, I thought, why not use the LED tape itself instead for
power and data transmission? Wires are heavy, expensive, and annoying to solder, so if I could find a way to
interconnect the LED tape so that it can all be driven from a driver circuit located at one of the character's
junctions while simultaneously powering that driver circuit, an 8seg character wouldn't need any wires at all anymore.
8seg achieves this feat using a circuit as shown in the diagram below. Interconnections between the LED tape segments
are done with a small circuit board in each of the four corners. The design is rotationally symmetric, and all four of
these boards are identicaly. The top right and bottom left corners simply use the back side of the same circuit board
used in the top left and bottom right corners.
.. image:: 8seg-digit-circuit.png
The driver circuit sits at the center of the character and directly connects to the four diagonal segments. The key
thought behind 8seg's driving scheme is that there are two common phases wound through the display in a zig-zag pattern
as shown in red and blue in the schema below. These phases alternate their polarity at a high frequency. Each segment
has its negative pole connected to one of these two phases, and can be turned on by the driver while that phase is low
and the other phase is high. While a phase is high, the LEDs on all segments connected to that phase are reverse-biased,
and thus these segments remain dark.
The positive poles of all segments are connected to the driver circuit in the center through a spiral pattern. Each arm
of the spiral is made up of two segments, one diagonal on the inside, and one horizontal or vertical on the outside.
The two segments on each spiral arm are on different phases, one on each of the two phases. Thus, during a single cycle
of the two phases alternating polarity, first one of the two segments has its polarity the right way around, then the
other. The driver can turn on the active segment by connecting the spiral control line to the positive LED supply
voltage.
Both phases cross at the center where the driver circuit is located, so the driver can power itself from the two phases
using a simple full bridge rectifier.
Saving copper with point of load regulation
-------------------------------------------
In the beginning, I experimented with the design above, putting 12V AC on the two phases, and letting the driver switch
its derived LED supply using some cheap MOSFETs. This simple design totally works, but it has an important shortcoming.
8seg is designed to be physically *very* large. This means that not only does it have a large number of LEDs that
together need a lot of current, it also has to transmit all of that current across significant physical distances. The
consequence of this was that in the initial design, I was looking at either needing hundreds of Euros worth of copper
cables, or burning hundreds of Watts of electricity into heat if I were to use thinner cables. In this case, cables act
like resistors. In a resistor, power dissipation rises with the square of the current inside the cable. This is bad for
8seg since it means halving the amount of copper in those wires increases power dissipation in these wires fourfold.
Despite that downside, this square law does come with an upside, too. If we assume we have wires of a particular fixed
diameter, if we can halve the current through those wires, we can quarter the wires' power dissipation. If we want to
deliver the same amount of power to the LEDs as before, to halve wire current, we have to double the voltage, and add
some circuitry on the drivers to convert that increased voltage back down to close to our LED tape's nominal 12V.
Alas, simply doubling the voltage leads to one question: How is it that we can pass double the voltage through our LED
tape to the center control circuit? Isn't the LED tape made for 12V operation only, not 24V? The answer to this
apparent problem is that the center is connected to the AC bus voltage only through the negative side of the LED tapes,
and controls their positive sides to turn them on or off. The AC bus voltage never appears directly across any single of
the eight segments. At the same time, a simple buck converter stepping down our new 24V bus voltage to 12V, and feeding
the segment control transistors with that instead of feeding them straight from the rectified AC bus allows us to feed
the segments with 12V. The only difference between this circuit and the straight 12V variant is that now, during OFF
times, the LED tapes see a negative 24 v across them. To make sure that's not a problem, I tested a number of them with
different LED colors and from different manufacturers, and all of them held up past the 50 V I could easily generate
with my lab power supply.
Synchronous rectification
-------------------------
I implemented the point-of-load regulation in a new revision of the center circuit, and built a prototype digit. When I
tested this prototype, to my dismay, I noticed some really strange behavior. In my tests, the LED tape did not properly
light up, and when I checked the voltages with my oscilloscope, I noticed that the center circuit's ground was floating
several volts *below* the AC bus voltage's negative phase. How come?
After some head-scratching, I found that this problem was due to a simple instance of Kirchhoff's current law. Consider
the point where the AC bus voltage's currently negative phase enters the center circuit board. Let's say that we
dissipate 24 Watts in the segments' LEDs. In this case, at 24 Volts, 1 Ampère will flow into the center circuit's
terminal connected to the currently positive phase, and out from the center circuit's terminal connected to the
currently negative phase.
Now consider the current through the LED tape. During one half-cycle of the AC bus, the center circuit can only address
the four segments that have their negative rail connected to the currently negative phase of the AC bus. If one of these
four segments is currently on and dissipating our 24 Watts, that segment will be fed 2 Ampère of current from the center
circuit through its positive rail. My mistake was that I did not consider what happened to the return current here.
The corresponding 2 Ampère return current of course flows back through the segment's negative rail into the center
circuit, and herein lies the issue: That negative rail is where our center circuit's supply current comes from! This
means that according to Kirchhoff's current law, the 1 A flowing out from the center circuit at its input are adding up
with the 2 A flowing into it. The result of this is that in the currently positive phase's connection, we get 1 A
flowing into the center circuit, while in the negative phase connection, we get (-1) + (+2) resulting in another 1 A
flowing into it! The only terminal where current flows *out* of the center circuit is the positive terminal connected to
the active segment, out of which 2 A of current are flowing.
The big problem with this confusing scenario is that this means the bridge recitifier in our center circuit cannot work,
since its negative-side diodes are reverse biased while any of the segments are on. We can't just add more diodes here,
since that would just short both AC bus rails together. Instead, the solution is to add one rather chonky MOSFET in
parallel with each of the two negative-side diodes of the bridge rectifier that are controlled by the center circuit to
act as a sort of synchronous rectifier. When we turn on one of the segments, we have to turn on the MOSFET on the
currently negative rail to allow the segment's return current to bypass the bridge rectifier's negative-side diode. Fun
fact: If we turn on the wrong MOSFET out of the pair, we short the AC bus, resulting in a very quick end of life for that
poor MOSFET.
Power line data communication
-----------------------------
As we saw above, the driver providing power to a string of digits has to continuously alternate the polarity of its
output voltage to provide one part of the digit circuits' multiplexing. Since we want to provide the control information
to the center circuits through those same two wires, we can choose between a number of viable power line communication
schemes. These schemes usually require a beefy transmitter adding a modulation at a frequency much larger than the
underlying bus frequency, and a filter circuit at each receiver to filter that signal from the much stronger fundamental
AC waveform. In our application, I saw two issues with these classical approaches. First, they require fairly complex
circuitry, especially the beefy transmitter at the driver. Second, they are susceptible to attenuation with either
changing load or over long distances, which could potentially be a problem with the high currents and long(ish) wiring
runs 8seg needs.
Because of these disadvantages, I decided on another approach entirely. Instead of modulating our control signal on top
of the AC power waveform, we modulate our control data *into* the AC power waveform. To not interfere with the display
and cause outages or flicker, and to avoid having to blank the display during transmissions, we choose a modulating
technique that leaves the proportions of negative and positive half-waves undisturbed. The practical realization of this
is that instead of alternating positive and negative half-waves, we send a positive half wave for each "one" bit, and a
negative half wave for each "zero" bit, effectively creating a phase shift keyed signal with two states with an
180-degree phase shift, with the transmitted bit rate synchronized to twice the underlying carrier frequency.
The remaining question is how one can encode arbitrary binary data into a continuous stream of ones and zeros that is
precisely 50 % ones and 50 % zeros across any time span longer than a few dozen bits. There exists a near-optimal
solution to this question from ethernet over copper twisted pairs. In ethernet, the encoded and modulated signal passes
through an isolation transformer to protect the ethernet transceiver from interference or dangerous voltages coming in
through the ethernet port. For this isolation transformer to work, the modulated ethernet signal must be exactly
balanced to avoid saturating the transformer's core with a DC offset. Ethernet solves this issue by using an encoding
known as `8b/10b encoding`_. 8b/10b encoding is named like that because it specifies a way to produce a 10 bit codeword
from any 8 bit input data word while guaranteeing that the resulting codewords are always precisely balanced when
looking at two or more consecutively.
Framing
-------
Since 8b/10b encoding maps a space of 256 data words to 1024 code words, there necessarily are a number of unused code
words. While for some of them, leaving them unallocated is beneficial because it improves error tolerance by decreasing
the probability of one code word turning into another undetectably when a single one of its bits is flipped, even
accounting for that it leaves some room for other uses. In 8b/10b, these leftover code words are used for synchronizing
the receiver to the transmitter, and for framing transmissions. Synchronization is necessary for the receiver to know
where a code word stards, and 8b/10b has a handful of special "comma" code words that can be uniquely identified in a
continuous stream of received ones and zeros, because no other combination of 8b/10b code words could produce the same
sequence of ones and zeros of the comma code word anywhere.
The leftover code words that are not commas are useful, too. They can be used, for instance, as filler code words
betwene actual data transmissions, or to act as framing markers denoting things like the end of a protocol message.
The 8seg driver produces its modulation waveform by translating all data to be transmitted into 8b/10b codes, padding
the result with framing markers and filler codes, and copy-pasting together the corresponding AC waveform from a small
set of pre-programmed waveform transitions.
.. _matelight: https://github.com/jaseg/matelight
.. _`8b/10b encoding`: https://en.wikipedia.org/wiki/8b/10b_encoding

3
content/blog/_index.rst Normal file
View file

@ -0,0 +1,3 @@
---
title: Blog
---

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="60mm"
height="18mm"
viewBox="0 0 60 18"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<rect
x="73.275604"
y="7.5154462"
width="82.669907"
height="60.687229"
id="rect1" />
</defs>
<g
id="layer1"
transform="translate(-60.324998,-47.365882)">
<path
id="path4-6"
style="fill:#ececec;stroke-width:0.105392;stroke-linecap:round;stroke-linejoin:round;fill-opacity:1"
d="m 69.324799,48.865886 a 7.5,7.5 0 0 0 -7.499801,7.499801 7.5,7.5 0 0 0 3.273185,6.195487 c -0.913481,-0.983452 -1.40067,-2.340488 -1.315165,-3.825089 0.198575,-3.447783 1.631942,-4.848283 1.631942,-4.848283 l 1.590084,3.588411 c -0.48406,-2.540144 -0.201121,-4.995971 2.766756,-8.596891 a 7.5,7.5 0 0 0 -0.447001,-0.01344 z m 1.328084,0.118339 c 0.310055,1.43067 1.013609,4.202913 2.107882,5.883879 1.029143,1.580919 1.930339,2.092129 1.865519,4.55166 -0.03918,1.486422 -0.657454,2.712199 -1.732194,3.542419 a 7.5,7.5 0 0 0 3.931026,-6.596496 7.5,7.5 0 0 0 -6.172233,-7.381462 z" />
<text
xml:space="preserve"
transform="matrix(0.67004375,0,0,0.67004375,30.304048,47.575216)"
id="text1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.3333px;line-height:13.9843px;font-family:Equateur;-inkscape-font-specification:Equateur;text-decoration-color:#000000;writing-mode:lr-tb;direction:ltr;white-space:pre;shape-inside:url(#rect1);display:inline;fill:#f9f9f9;fill-opacity:1;stroke-width:0.755906;stroke-linecap:round;stroke-linejoin:round"><tspan
x="73.275391"
y="19.8411"
id="tspan2">ashen</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="60mm"
height="18mm"
viewBox="0 0 60 18"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<rect
x="73.275604"
y="7.5154462"
width="82.669907"
height="60.687229"
id="rect1" />
</defs>
<g
id="layer1"
transform="translate(-60.324998,-47.365882)">
<path
id="path4-6"
style="fill:#000000;stroke-width:0.105392;stroke-linecap:round;stroke-linejoin:round"
d="m 69.324799,48.865886 a 7.5,7.5 0 0 0 -7.499801,7.499801 7.5,7.5 0 0 0 3.273185,6.195487 c -0.913481,-0.983452 -1.40067,-2.340488 -1.315165,-3.825089 0.198575,-3.447783 1.631942,-4.848283 1.631942,-4.848283 l 1.590084,3.588411 c -0.48406,-2.540144 -0.201121,-4.995971 2.766756,-8.596891 a 7.5,7.5 0 0 0 -0.447001,-0.01344 z m 1.328084,0.118339 c 0.310055,1.43067 1.013609,4.202913 2.107882,5.883879 1.029143,1.580919 1.930339,2.092129 1.865519,4.55166 -0.03918,1.486422 -0.657454,2.712199 -1.732194,3.542419 a 7.5,7.5 0 0 0 3.931026,-6.596496 7.5,7.5 0 0 0 -6.172233,-7.381462 z" />
<text
xml:space="preserve"
transform="matrix(0.67004375,0,0,0.67004375,30.304048,47.575216)"
id="text1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.3333px;line-height:13.9843px;font-family:Equateur;-inkscape-font-specification:Equateur;text-decoration-color:#000000;writing-mode:lr-tb;direction:ltr;white-space:pre;shape-inside:url(#rect1);display:inline;fill:#000000;fill-opacity:1;stroke-width:0.755906;stroke-linecap:round;stroke-linejoin:round"><tspan
x="73.275391"
y="19.8411"
id="tspan2">ashen</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,59 @@
---
title: "Project Announcement: Ashen and Yanartas"
date: 2026-05-31T08:00:00+02:00
summary: >
There are exciting things ahead for the next year: I have been granted funding by both nlnet and by prototype fund
for open-source work on an open source hardware Hardware Security Module. As a vessel for this project, I created a
consulting company, yasec.
---
I'm currently in the last days of finishing my PhD (Dr.-Ing.) in Electrical Engineering. To make sure things don't get
boring afterwards, I've been busy looking for new opportunities. As a result, there are exciting things ahead for the
next year: I have been granted funding by both nlnet *and* by Prototype Fund for open-source work on an Open Source
Hardware Hardware Security Module. As infrastructure for these projects, I created a consulting company, `yasec
<https://yasec.de>`__.
Prototype Fund supports Ashen, the OS for open-source HSMs
----------------------------------------------------------
.. raw:: html
<picture style="max-width: 10em; margin: 1em auto 1em auto">
<source media="(prefers-color-scheme: light)" srcset="ashen-logo-text-light-plain.svg">
<img src="ashen-logo-text-dark-plain.svg alt="Ashen project logo showing a stylized flame in a circle">
</picture>
Starting June 2026, I will be working on Ashen_, an open-source software stack that provides the operating system layer
for open-source HSMs. The project is funded as part of `Prototype Fund`_'s Class 02.
Compared to existing open-source HSM software that work at the application level and that don't
consider physical attacks, this stack will provide the underlying operating system services to protect such systems from
physical attacks. A key component of this stack will be a portable mechanism to connect hardware tamper sensors to a
system. The stack will enable deterministic guarantees of the maximum latency until secrets are destroyed after a tamper
alarm was raised.
.. _Ashen: https://yasec.de/projects/ashen/
.. _`Prototype Fund`: https://www.prototypefund.de/
nlnet supports Yanartas, the OSHW HSM platform
----------------------------------------------
After work on the Ashen software stack is completed, I will continue by creating Yanartas_, an Open Source Hardware
design for a complete open-source Hardware Security Module that provides protection against advanced physical attacks
using a security mesh based on the `Inertial HSM`_ technology I developed during my PhD. The design will be customizable
to different use cases and payload sizes from microcontrollers to whole servers.
.. _Yanartas: https://yasec.de/projects/yanartas/
.. _`Inertial HSM`: https://tches.iacr.org/index.php/TCHES/article/view/9290
Let's talk!
-----------
In case you're interested to talk about hardware security engineering or open-source hardware, feel free to reach out
through email or on mastodon. The projects are in an early stage, and I'm looking both for collaborators for these
projects, and for opportunities once these projects have been completed. At this time, I only have a small amount of
spare capacity outside of these projects, but that will change with time. I'd love to hear about *your* projects and
your needs for specialist work in case you're interested.
Follow this blog's `RSS <https://jaseg.de/index.xml>`__ and follow me `on mastodon <https://chaos.social/@jaseg>`__ for
updates!

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="35mm"
height="17.549mm"
viewBox="0 0 35 17.549"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<rect
x="33.844814"
y="193.19748"
width="593.69446"
height="361.01135"
id="rect1" />
</defs>
<g
id="layer1"
transform="translate(-30.000001,-82.703)">
<path
style="font-size:21.3333px;line-height:13.9843px;font-family:Equateur;-inkscape-font-specification:Equateur;white-space:pre;fill:#e0e0e0;stroke-width:0.755906;stroke-linecap:round;stroke-linejoin:round"
d="m 33.611037,211.68229 v 0.10667 h 3.583994 l 7.082656,-15.33865 1.87733,-0.55466 v -0.10667 h -5.503991 v 0.10667 l 2.111996,0.53333 -2.218663,6.82666 -3.135995,-6.84799 2.239997,-0.512 v -0.10667 h -6.549323 v 0.10667 l 1.68533,0.512 4.543993,9.66398 c -1.727997,2.816 -3.135995,4.416 -5.717324,5.61066 z m 14.762647,-5.11999 c 1.151998,0 1.834664,-0.74667 2.794662,-1.45067 0.277333,0.78934 0.853332,1.42934 1.642664,1.42934 1.087999,0 1.941331,-1.216 2.474663,-2.112 l -0.08533,-0.0853 c -0.256,0.256 -0.874665,0.76799 -1.237331,0.76799 -0.341333,0 -0.490666,-0.46933 -0.490666,-1.13066 v -5.63199 c 0,-1.408 -1.130665,-2.816 -2.51733,-2.816 -1.62133,0 -5.205325,1.94133 -5.205325,2.83733 0,0.66133 1.919997,1.81333 3.093329,2.368 l 0.08533,-0.10667 c -0.511999,-0.704 -1.045332,-1.83466 -1.045332,-2.53866 0,-0.896 0.895999,-1.536 1.727997,-1.536 1.066665,0 1.471998,1.024 1.471998,2.24 v 2.21866 l -4.906659,1.25867 c 0,0 -0.447999,0.85333 -0.447999,1.79199 0,1.68533 1.450664,2.496 2.645329,2.496 z m -0.362666,-3.05066 c 0,-0.384 0.08533,-0.85334 0.08533,-0.85334 l 2.986662,-1.13066 v 3.072 c -0.554666,0.31999 -0.981332,0.61866 -1.578664,0.61866 -0.895999,0 -1.493331,-0.704 -1.493331,-1.70666 z m 11.818665,3.09333 c 1.727997,0 4.053327,-1.088 4.053327,-3.37067 0,-0.95999 -0.405333,-1.91999 -1.535998,-2.47466 l -2.794662,-1.38666 c -0.725332,-0.36267 -1.194665,-0.832 -1.194665,-1.536 0,-0.896 0.746666,-1.62133 1.813331,-1.62133 1.578664,0 2.19733,1.62133 2.090663,3.11466 l 0.128,0.0427 1.301331,-2.88 c -0.639999,-0.49066 -2.090663,-0.87466 -3.199995,-0.87466 -1.770663,0 -3.839994,0.98133 -3.839994,3.22132 0,0.896 0.341333,1.92 1.450665,2.45333 l 2.794662,1.344 c 0.746666,0.36267 1.151998,0.85333 1.151998,1.55733 0,1.00267 -0.810665,1.83467 -1.919997,1.83467 -1.855997,0 -3.007995,-2.38933 -2.794662,-4.11733 l -0.106666,-0.0427 -1.514665,3.28533 c 0.810666,0.74667 2.709329,1.45067 4.117327,1.45067 z m 9.96266,0 c 2.111996,0 3.221328,-1.152 4.245326,-2.688 L 73.90967,203.8103 c -0.938666,0.96 -1.813331,1.42933 -2.922662,1.42933 -2.51733,0 -3.669328,-2.43199 -3.669328,-4.69332 0,-0.17067 0,-0.32 0.02133,-0.46933 l 6.186657,-0.68267 c 0,-2.09066 -0.725332,-3.83999 -3.157328,-3.83999 -2.346663,0 -4.415993,1.94133 -5.077325,4.37332 l -1.407998,0.384 0.02133,0.128 1.279998,-0.14933 c -0.08533,0.42666 -0.128,0.85333 -0.128,1.28 0,2.92266 1.855997,5.03466 4.735993,5.03466 z m -2.38933,-7.25333 c 0.256,-1.94133 1.194665,-3.02933 2.431996,-3.02933 0.895999,0 1.493331,0.93867 1.557331,1.92 z m 12.181323,7.25333 c 1.919997,0 3.498661,-1.00267 4.586659,-2.688 l -0.128,-0.10667 c -0.895998,0.896 -2.00533,1.42933 -3.221328,1.42933 -2.751996,0 -3.797327,-2.73066 -3.797327,-4.86399 0,-2.23999 1.173331,-3.98933 2.837329,-3.98933 1.130665,0 1.87733,0.98134 1.87733,2.176 0,0.832 -0.234666,1.77067 -0.511999,2.41066 l 0.106666,0.0853 c 0.810666,-0.85333 2.495996,-2.41067 2.495996,-3.456 0,-1.06666 -1.151998,-2.06933 -2.986662,-2.06933 -3.370661,0 -6.165323,2.41067 -6.165323,5.84533 0,2.87999 1.962663,5.22666 4.906659,5.22666 z"
id="text1"
transform="matrix(0.58740671,0,0,0.58740671,12.90413,-25.337712)"
aria-label="yasec" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="35mm"
height="17.549mm"
viewBox="0 0 35 17.549"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1">
<rect
x="33.844814"
y="193.19748"
width="593.69446"
height="361.01135"
id="rect1" />
</defs>
<g
id="layer1"
transform="translate(-30.000001,-82.703)">
<path
style="font-size:21.3333px;line-height:13.9843px;font-family:Equateur;-inkscape-font-specification:Equateur;white-space:pre;stroke-width:0.755906;stroke-linecap:round;stroke-linejoin:round"
d="m 33.611037,211.68229 v 0.10667 h 3.583994 l 7.082656,-15.33865 1.87733,-0.55466 v -0.10667 h -5.503991 v 0.10667 l 2.111996,0.53333 -2.218663,6.82666 -3.135995,-6.84799 2.239997,-0.512 v -0.10667 h -6.549323 v 0.10667 l 1.68533,0.512 4.543993,9.66398 c -1.727997,2.816 -3.135995,4.416 -5.717324,5.61066 z m 14.762647,-5.11999 c 1.151998,0 1.834664,-0.74667 2.794662,-1.45067 0.277333,0.78934 0.853332,1.42934 1.642664,1.42934 1.087999,0 1.941331,-1.216 2.474663,-2.112 l -0.08533,-0.0853 c -0.256,0.256 -0.874665,0.76799 -1.237331,0.76799 -0.341333,0 -0.490666,-0.46933 -0.490666,-1.13066 v -5.63199 c 0,-1.408 -1.130665,-2.816 -2.51733,-2.816 -1.62133,0 -5.205325,1.94133 -5.205325,2.83733 0,0.66133 1.919997,1.81333 3.093329,2.368 l 0.08533,-0.10667 c -0.511999,-0.704 -1.045332,-1.83466 -1.045332,-2.53866 0,-0.896 0.895999,-1.536 1.727997,-1.536 1.066665,0 1.471998,1.024 1.471998,2.24 v 2.21866 l -4.906659,1.25867 c 0,0 -0.447999,0.85333 -0.447999,1.79199 0,1.68533 1.450664,2.496 2.645329,2.496 z m -0.362666,-3.05066 c 0,-0.384 0.08533,-0.85334 0.08533,-0.85334 l 2.986662,-1.13066 v 3.072 c -0.554666,0.31999 -0.981332,0.61866 -1.578664,0.61866 -0.895999,0 -1.493331,-0.704 -1.493331,-1.70666 z m 11.818665,3.09333 c 1.727997,0 4.053327,-1.088 4.053327,-3.37067 0,-0.95999 -0.405333,-1.91999 -1.535998,-2.47466 l -2.794662,-1.38666 c -0.725332,-0.36267 -1.194665,-0.832 -1.194665,-1.536 0,-0.896 0.746666,-1.62133 1.813331,-1.62133 1.578664,0 2.19733,1.62133 2.090663,3.11466 l 0.128,0.0427 1.301331,-2.88 c -0.639999,-0.49066 -2.090663,-0.87466 -3.199995,-0.87466 -1.770663,0 -3.839994,0.98133 -3.839994,3.22132 0,0.896 0.341333,1.92 1.450665,2.45333 l 2.794662,1.344 c 0.746666,0.36267 1.151998,0.85333 1.151998,1.55733 0,1.00267 -0.810665,1.83467 -1.919997,1.83467 -1.855997,0 -3.007995,-2.38933 -2.794662,-4.11733 l -0.106666,-0.0427 -1.514665,3.28533 c 0.810666,0.74667 2.709329,1.45067 4.117327,1.45067 z m 9.96266,0 c 2.111996,0 3.221328,-1.152 4.245326,-2.688 L 73.90967,203.8103 c -0.938666,0.96 -1.813331,1.42933 -2.922662,1.42933 -2.51733,0 -3.669328,-2.43199 -3.669328,-4.69332 0,-0.17067 0,-0.32 0.02133,-0.46933 l 6.186657,-0.68267 c 0,-2.09066 -0.725332,-3.83999 -3.157328,-3.83999 -2.346663,0 -4.415993,1.94133 -5.077325,4.37332 l -1.407998,0.384 0.02133,0.128 1.279998,-0.14933 c -0.08533,0.42666 -0.128,0.85333 -0.128,1.28 0,2.92266 1.855997,5.03466 4.735993,5.03466 z m -2.38933,-7.25333 c 0.256,-1.94133 1.194665,-3.02933 2.431996,-3.02933 0.895999,0 1.493331,0.93867 1.557331,1.92 z m 12.181323,7.25333 c 1.919997,0 3.498661,-1.00267 4.586659,-2.688 l -0.128,-0.10667 c -0.895998,0.896 -2.00533,1.42933 -3.221328,1.42933 -2.751996,0 -3.797327,-2.73066 -3.797327,-4.86399 0,-2.23999 1.173331,-3.98933 2.837329,-3.98933 1.130665,0 1.87733,0.98134 1.87733,2.176 0,0.832 -0.234666,1.77067 -0.511999,2.41066 l 0.106666,0.0853 c 0.810666,-0.85333 2.495996,-2.41067 2.495996,-3.456 0,-1.06666 -1.151998,-2.06933 -2.986662,-2.06933 -3.370661,0 -6.165323,2.41067 -6.165323,5.84533 0,2.87999 1.962663,5.22666 4.906659,5.22666 z"
id="text1"
transform="matrix(0.58740671,0,0,0.58740671,12.90413,-25.337712)"
aria-label="yasec" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -0,0 +1,209 @@
---
title: "Code listings with nice line wrapping and line numbers from plain CSS"
date: 2025-07-23T23:42:00+01:00
summary: >
Code listings in web pages are often a bit of a pain to use. Usually, they don't wrap on small screens. Also,
copy-pasting code from a code listing often copies the line numbers along with the code. Finally, many
implementations use heavyweight HTML and/or javascript, making them slow to render. For this blog, I wrote a little
CSS hack that renders nice, wrapping code blocks with line continuation markers in plain CSS without any JS.
---
Code listings in web pages are often a bit of a pain to use. Often, they don't wrap on small screens. Also, copy-pasting
code from a code listing often copies the line numbers along with the code. Finally, many implementations use
heavyweight HTML and/or javascript, making them slow to render (looking at you, gitlab).
For this blog, I wrote an implementation that renders HTML code listings entirely without JavaScript, renders line
numbers using plain CSS such that they don't get selected with the code, and that works with the browser to wrap in a
natural way while still supporting the little line continuation arrows that are used to show that a line was soft
wrapped in text editors.
This blog is rendered as a static site using Hugo_ from a pile of RestructuredText_ documents. RestructuredText renders
code listings using Pygments_ by default. Pygments hard-bakes the line numbers into the generated HTML, so I am using a
`monkey-patched`_ hook that changes the line number rendering to just a bunch of empty ``<span>`` elements. The resulting
HTML for a code block then looks like this:
.. code:: html
<pre class="code [language] literal-block">
<span class="lineno"></span>
<span class="line">
<span class="[syntax highlight token]">The </span><span class="[other syntax highlight token]">code!<span>
</span>
<!-- ... repeat once for each source line. -->
</pre>
You can find the (rather short) source of the ``rst2html`` wrapper `below <#rst2html-wrapper>`_.
The CSS
-------
This modified HTML structure of the code listing gets accompanied by some CSS to make it flow nicely. Here is a listing
of the complete CSS controlling the listing. The only bit that isn't included here is the actual syntax styling rules
for the pygments tokens.
.. code:: css
/*****************************************************/
/* Code block formatting / syntax highlighting rules */
/*****************************************************/
.code {
font-family: "Fira Code";
font-size: 13px;
text-align: left; /* Override default content "justify" alignment */
white-space: pre-wrap;
word-wrap: break-word;
overflow-x: auto;
display: grid;
align-items: start;
grid-template-columns: min-content 1fr;
}
.code > .line {
padding-left: calc(2em + 5px);
text-indent: -2em;
padding-top: 2px;
min-width: 15em;
}
/* Make individual syntax tokens wrap anywhere */
.code > .line > span {
overflow-wrap: anywhere;
white-space: pre-wrap;
}
/* We render line numbers in CSS! */
.code > .lineno {
counter-increment: lineno;
word-break: keep-all;
margin: 0;
padding-left: 15px;
padding-right: 5px;
overflow: clip;
position: relative;
text-align: right;
color: var(--c-text-muted);
border-right: 1px solid var(--c-fg-highlight);
align-self: stretch;
}
/* We also handle line continuation markers in CSS. */
.code > .lineno::after {
position: absolute;
right: 5px;
content: "\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳";
white-space: pre;
color: var(--c-text-muted);
}
/* Insert the actual line number */
.code > .lineno::before {
content: counter(lineno);
}
.code::before {
counter-reset: lineno;
}
.code .hll {}
/* Following are about 50 lines that define the styling of each kind of pygments syntax highlight token. These lines
all look like the following: */
.code .c { color: var(--c-text); font-weight: 400 } /* Comment */
This CSS does a few things:
1. It renders the ``<pre>`` code listing element using a two-column CSS ``display: grid`` layout. The left column is
used for the line numbers, and the right column is used for the code lines.
2. It numbers the lines using a `CSS Counter`_. CSS counters are meant for things like numbering headings and such, but
they are a perfect fit for our purpose.
3. It inserts the counter value as the line number into the ``<span class="lineno">`` element's ``::before``
pseudo-element. A side effect of using the ``::before`` pseudo-element is that without doing anything extra, the
line numbers will remain outside of the normal text selection so they will neither be highlighted when selecting
listing content, nor will they be copied when copy/pasting the listing content.
4. It inserts a string of ``"\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳"`` into the line number span's
``::after`` pseudo-element. This string evaluates to a sequence of unicode arrows separated by line breaks, and
starting with an empty line. The ``::after`` pseudo-element is positioned using ``position: absolute``, and the
parent ``<span class="lineno">`` has ``position: relative`` set. This way, the arrow pseudo-element gets placed on
top of the lineno span without affecting the layout at all. By setting ``overflow: clip`` on the parent ``<span
class="lineno">``, the arrow pseudo-element gets cut off vertically wherever the parent lineno element naturally
ends.
The line number span is inserted into the parent ``<pre>`` element's CSS grid using ``align-self: stretch``, which
causes it to vertically stretch to fill the available space. Since the line number span only contains the line number,
its minimum height is a single line. As a result, it will stretch higher only when the corresponding code line in the
right grid column stretches vertically because of line wrapping. When that happens, part of the arrow pseudo-element
starts showing through from behind the ``overflow: clip`` of the line number span, and one arrow gets rendered for each
wrapped listing line.
When the page is too narrow, we don't want the code listing's lines to wrapp into a column of single characters. To
prevent that, we simply set a ``min-width`` on the ``<span class="line">`` in the right column, and set ``overflow-x:
auto`` on the listing ``<pre>``. This results in a horizontal scroll bar appearing whenever the listing gets too narrow.
You can try out the line wrapping by resizing this page!
rst2html wrapper
----------------
Here is the python ``rst2html`` wrapper that monkey-patches code rendering. I made hugo invoke this while building the
page by simply overriding the ``PATH`` environment variable.
.. code:: python
#!/usr/bin/env python3
# Based on https://gist.github.com/mastbaum/2655700 for the basic plugin scaffolding
import sys
import re
import docutils.core
from docutils.transforms import Transform
from docutils.nodes import TextElement, Inline, Text
from docutils.parsers.rst import Directive, directives
from docutils.writers.html4css1 import Writer, HTMLTranslator
class UnfuckedHTMLTranslator(HTMLTranslator):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.in_literal_block = False
def visit_literal_block(self, node):
# Insert an empty "lineno" span before each line. We insert the line numbers using pure CSS in a ::before
# pseudo-element. This has the added advantage that the line numbers don't get included in text selection.
# These line number spans are also used to show line continuation markers when a line is wrapped.
self.in_literal_block = True
self.body.append(self.starttag(node, 'pre', CLASS='literal-block'))
self.body.append('<span class="lineno"></span><span class="line">')
def depart_literal_block(self, node):
self.in_literal_block = False
self.body.append('\n</span></pre>\n')
def visit_Text(self, node):
if self.in_literal_block:
for match in re.finditer('([^\n]*)(\n|$)', node.astext()):
text, end = match.groups()
if text:
super().visit_Text(Text(text))
if end == '\n':
if isinstance(node.parent, Inline):
self.depart_inline(node.parent)
self.body.append(f'</span>\n<span class="lineno"></span><span class="line">')
if isinstance(node.parent, Inline):
self.visit_inline(node.parent)
else:
super().visit_Text(node)
html_writer = Writer()
html_writer.translator_class = UnfuckedHTMLTranslator
docutils.core.publish_cmdline(writer=html_writer)
.. _Hugo: https://gohugo.io/
.. _RestructuredText: https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html
.. _Pygments: https://pygments.org/
.. _`monkey-patched`: https://en.wikipedia.org/wiki/Monkey_patch
.. _`CSS Counter`: https://developer.mozilla.org/en-US/docs/Web/CSS/counter

View file

@ -0,0 +1,100 @@
---
title: "75 Million Lives, Two Keys"
date: 2025-01-05T23:42:00+01:00
draft: true
---
2025 has begun. In this new year, with its new national healthcare record system, the country of Germany will start one
of the largest rollouts of a cryptographic system in history. While the system has received scrutiny as well as
resulting harsh criticism from a number of parties ranging from NGOs to everyday civilians, the system has received
surprisingly little attention from the academic applied cryptography crowd. Additionally, previous criticism of
the system has largely revolved around organizational issues. While valid, we belive that some cryptographic issues at
the core of the system have escaped attention unitl now. In particular, at the core of the system is a key escrow system
that contains several questionable design choices and that in its overall design seems out of place in 2025.
The aim of the system is to serve as a shared storage for all healthcare records of a person. In the system, a person's
entire patient file with all documentation on the treatment process including test results, images and other raw data
will be stored in something vaguely resembling cloud storage such that all healthcare providers that the person visits
can access the entire file. This centralized, synchronized storage eliminates the need for transferring this data
between hospitals and doctors offices by fax, mail or physical media as it was common practice until now. After a
development and testing phase lasting approximately five years, the German government decided to roll out the system to
everybody insured under Germany's mandatory national health insurance scheme, totalling approximately 75 million people,
on January 15th 2025.
In this article, we will give an overview of the system's cryptographic design before highlighting a few odd
design choices that could amount to a viable attack vector to the powerful adversaies
## Context and involved parties
Germany has a national, mandatory health insurance system. The system is open to any permanent resident of the country
irrespective of citizenship. The system is mandatory in that while residents can choose between a number of both
publically owned as well as private healthcare providers, it is not possible to opt out of the system. The public health
insurance providers cover approximately 90% of German residents. These providers are organized in an umbrella
organization named "GKV Spitzenverband". The resposibility of this umbrella organization largely revolves around
negotiating prices with pharmaceutical companies and with healthcare providers as a publically sanctioned cartel, but
also includes the specification and operation of shared IT infrastructure for billing and data exchange between
healthcare providers.
While GKV Spitzenverband is the party that ultimately holds responsibility for the regulatory administration of national
healthcare IT infrastructure, it has delegated large parts of both the technical specification of this infrastructure as
well as its day-to-day operation to Gematik GmbH, a state-owned limited liability corporation created specifically for
the purpose of developing and implementing national healthcare IT standards. The electronic healthcare record system we
describe in this article was standardized and implemented by Gematik GmbH under the direction of GKV Spitzenverband.
Healthcare providers in Germany need to be registered with GKV Spitzenverband to serve members of public health
insurance providers. Since these public providers constitute approximately 90% market share, the vast majority of
healthcare providers are registered this way.
Before the new national health record system, a number of healthcare IT processes have already been standardized and
implemented by the parties above. In particular, every insured person already owns a cryptographic smartcard that acts
as their proof of identity when accessing healthcare services. On the other side of such transactions, healthcare
providers are likewise identified by cryptographic smartcards. Until now, these cards were used to facilitate billing of
services from healthcare providers to insurers and to transfer prescriptions from prescribing doctors to pharmacies.
A central role in this existing infrastructure is assumed by VPN gateways that link healthcare providers to
the centrally-run backend infrastructure. Gematik GmbH calls these devices "Konnektor". They are specially-built
hardware devices that contain multiple smart cards to authenticate the VPN connection towards the backend, and besides
acting as a standard VPN gateway for client applications in the healthcare provider's network to tunnnel their backend
requests through, the Konnektors also perform cryptographic operations in some of Gematik GmbH's protocols, such as
authenticating certain requests using signatures.
## Design principles
The new health record system was built on top of the existing infrastructure described above. In particular, access to
health records is managed through keys stored in the patient's and the healthcare provider's existing smartcards, and
all backend communication is tunneled through the existing VPN. Access to the files is mediated through the healthcare
provider's existing patient management software. While in early drafts of the system, access to healthcare records
through the patient's smartcard was gated behind a PIN, the impracticality of making the entire patient populace
remember PINs led the implementors to scrap this provision, meaning that the patient's smartcard is all a healthcare
provider needs to access the patient's record.
A critical cornerstone in the system's design is that the system's designers decided that a lost smartcard should not
lead to any data loss. As a consequence of this decision, while some of the record's access keys are kept on the
patient smartcard, in contravention to conventional smartcard designs the same keys are kept accessible in a centralized
key escrow system named "Schlüsselgenerierungsdienst" and abbreviated as SGD. Furthermore, these keys are not generated
on the smartcard either -- instead, the key escrow system generates these access keys, one copy of which is then
transmitted and stored inside the smartcard.
The system supports re-issuing a smartcard to gain access to a healthcare record. Since the record's privacy pivots on
this process, the system incorporates some organziational countermeasures that aim to make it hard to gain access to a
re-issued copy of a patient smartcard without the patient's help or otherwise multiple colluding parties.
## Cryptographic design
## The implied adversary model
While Gematik GmbH publishes detailed specifications of the systems they standardize, these specifications and some
associated implementation guidelines are about the extent of public information. Software implementations are being kept
secret, and while standardization results are available, a large fraction of design rationale is discussed behind closed
doors. From an academic perspective, the most glaring omission in Gematik GmbH's public documents is any definition of a
threat model or an adversary model. As a result of this, we will deduce an adversary model below by contextualizing the
published standards in the national healthcare setting. We will base our further analysis of the system on this
adversary model.
## Previous reviews and audits of the system
[0] https://www.destatis.de/DE/Themen/Arbeit/Arbeitsmarkt/Qualitaet-Arbeit/Dimension-2/krankenversicherungsschutz.html

View file

@ -1,11 +1,11 @@
---
title: "Hardware Security Module Basics"
date: 2019-05-17T15:29:20+08:00
summary: >
I gave a short introduction into Hardware Security Modules at our university workgroup, including an overview on
interesting research directions.
---
Hardware Security Modules and Security Research and Cryptography
================================================================
On May 17 2019 I gave a short presentation on the fundamentals of hardware security modules at the weekly seminar of
Prof. Mori's security research working group at Waseda University. The motivation for this was that outside of low-level
hardware security people and people working in the financial industry HSMs are not thought about that often. In
@ -113,21 +113,26 @@ The core component of an HSM blueprint would be a suite of tamper detection mech
to improve on the current state of the art of membrane tamper switches plus temperature sensors plus PCB and printed
security meshes plus potting.
Improvements on existing techniques
-----------------------------------
Light sensors
~~~~~~~~~~~~~
**Advanced analog sensing**
**Self-test functionality**
Security meshes
~~~~~~~~~~~~~~~
**Analog sensing**
DIY or small lab mesh production
--------------------------------
**Analog sensing** meshes are a proven technology where instead of just monitoring for continuity and shorts, analog
parameters of the mesh traces such as inductance and mutual capacitance are monitored. In 2019, `Immler et al. published
a paper <https://tches.iacr.org/index.php/TCHES/article/view/7334>`__ where took this principle and turned it all the
way up. They directly derived a cryptographic secret from the analog properties of their HSM's security mesh in an
attempt to built a `Physically Unclonable Function, or PUF
<https://en.wikipedia.org/wiki/Physical_unclonable_function>`__. The idea with PUFs is that they reproduce some entropy
that comes from random tolerances of their production process. The same PUF will always yield (approximately) the same
key, but since you cannot control these random production variations, in practice the resulting PUF cannot be cloned.
Note however, that its secrets can of course be copied if you find a way to read them out.
As Immler et al. demonstrated in their paper, you don't need any secret sauce to create an analog mesh sensing circuit.
All you need are a bunch of (admittedly, expensive) off-the-shelf analog ICs. The interesting bit here is that by
applying more advanced analog sensing, weaknesses of an otherwise coarse mesh desing could maybe be alleviated. That is,
instead of monitoring a very fine mesh for continuity, you could instead closely monitor inductance and capacitance of a
more coarse mesh. This trade-off between sensing circuit complexity (resp. cost) and mesh production capabilities may
allow someone with a poorly equipped lab to still make a decent HSM. The question is, how do you produce a "decent" mesh
given only basic tools? Here are some ideas.
**3D metal patterning techniques** refers to any technique for producing thin, patterned metal structures on a
three-dimensional plastic substrate. The basic process would consist of 3D-printing the polymer substrate, depositing a
thin metal layer on top and then patterning this metal layer. A good starting point here would be the recent work of

View file

@ -0,0 +1,44 @@
---
title: "New Paper on Inertial Hardware Security Modules"
date: 2021-11-23T23:42:20+01:00
summary: >
Paper announcement: We have published a paper on how you can DIY a tamper-sensing hardware security module from any
single-board computer using a moving tamper-sensing mesh made from cheap PCBs.
---
World's First DIY HSM
=====================
Last week, Prof. Dr. Björn Scheuermann and I have `published our first joint paper on Hardware Security Modules
<https://tches.iacr.org/index.php/TCHES/article/view/9290>`__. In our paper, we introduce Inertial Hardware Security
Modules (IHSMs), a new way of building high-security HSMs from basic components. I think the technology we demonstrate
in our paper might allow some neat applications where some civil organization deploys a service that no one, not even
they themselves, can snoop on. Anyone can built an IHSM without needing any fancy equipment, which makes me optimistic
that maybe the ideas of the `Cypherpunk movement <https://www.activism.net/cypherpunk/manifesto.html>`__ aren't obsolete
after all, despite even the word "crypto" having been co-opted by radical capitalist environmental destructionists.
An IHSM is basically an ultra-secure enclosure for something like a server or a raspberry pi that even someone with
unlimited resources would have a really hard time cracking without destroying all data stored in it. The principle of an
IHSM is the same as that of a `normal HSM`_. You have a payload that contains really secret data. There's really no way
to prevent an attacker with physical access to the thing from opening it given enough time and abrasive discs for their
angle grinder. So what you do instead is that you make it self-destruct its secrets within microseconds of anyone
tampering with it. Usually, such HSMs are used for storing credit card pins and other financial data. They're expensive
as fuck, all the while being about the same processing speed as a smartphone. Traditional HSMs use printed or
lithographically patterned conductive foils for their security mesh. These foils are not an off-the-shelf component and
are made in a completely custom manufacturing process. To create your own, you would have to re-engineer that entire
process and probably spend some serious money on production machines.
Inertial HSMs take the concept of traditional HSMs, but replace the usual tamper detection mesh with a few security mesh
PCBs. These PCBs are coarser than traditional meshes by orders of magnitude, and would alone not even be close to enough
to keep out even a moderately motivated attacker. IHSMs fix this issue by spinning the entire tamper detection mesh at
very high speed. To tamper with the mesh, an attacker would have to stop it. This, in turn, can be easily detected by
the mesh's alarm circuitry using a simple accelerometer as a rotation sensor.
In our paper, we have shown a working prototype of the core concepts one needs to build such an IHSM. To build an IHSM
you only need a basic electronics lab. I built the prototype in our paper at home during one of Germany's COVID
lockdowns. You can have a look at our code and CAD on `my git <https://git.jaseg.de/ihsm.git>`__. What is missing right
now is an integration of all of these fragments into something cohesive that an interested person with the right tools
could go out and build. We are planning to release this sort of documentation at some point, but right now we are
focusing our effort on the next iteration of the design instead. Stay tuned for updates ;)
.. _`normal HSM`: {{<ref "blog/hsm-basics/index.rst">}}

View file

@ -0,0 +1,21 @@
---
title: "Getting the .ipynb Notebook File Location From a Running Jupyter Lab Notebook"
date: 2025-06-29T23:42:00+01:00
summary: >
If you need to get the path of the ipynb file in a running #Jupyter notebook, this one-liner will do the trick. It
seems chatgpt is confused, and a bunch of other approaches on the web look fragile and/or unnecessarily complex to
me.
---
If you need to get the path of the ipynb file in a running #Jupyter notebook, this one-liner will do the trick. It seems
chatgpt is confused, and a bunch of other approaches on the web look fragile and/or unnecessarily complex to me.
.. code:: python
import sys
Path(json.loads(Path(sys.argv[-1]).read_bytes())['jupyter_session'])
The way this works is that for each notebook, jupyter starts a python "kernel" process that actually runs the notebook's
code. That kernel gets a json file with info on the notebook's location on the disk passed through its command line.
Since we're running code in that exact python process, we can just grab that json file from sys.argv, and read it
ourselves.

View file

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 292 KiB

After

Width:  |  Height:  |  Size: 292 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 296 KiB

After

Width:  |  Height:  |  Size: 296 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 296 KiB

After

Width:  |  Height:  |  Size: 296 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 296 KiB

After

Width:  |  Height:  |  Size: 296 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 296 KiB

After

Width:  |  Height:  |  Size: 296 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 193 KiB

After

Width:  |  Height:  |  Size: 193 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 189 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 175 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 809 KiB

After

Width:  |  Height:  |  Size: 809 KiB

Before After
Before After

View file

@ -1,12 +1,16 @@
---
title: "Kicad Mesh Plugin"
date: 2020-08-18T13:15:39+02:00
summary: >
I wrote a little KiCad plugin that you can use to create security meshes, heaters and other things where you need
one or more traces cover the entire surface of a PCB. The plugin supports arbitrary PCB shapes, cutouts, and can
route around existing footprints and traces on the PCB.
---
.. raw:: html
<figure>
<img src="images/anim.webp" style="max-width: 20em">
<figure data-pagefind-ignore>
<img src="images/anim.webp" style="max-width: 20em">
</figure>
Tamper Detection Meshes
@ -38,7 +42,7 @@ stored on the card, things such as copying of a card can only be hindered by mak
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/modern_art.svg" style="max-width: 20em">
</figure>
@ -68,7 +72,7 @@ DIY Meshes
Throughout my studies in security research I have always had an interest in HSMs. I have taken apart my fair share of
HSMs and at this point, to understand the technology more, I want to experiment with building my own HSM. In last year's
`HSM basics <{{<ref "posts/hsm-basics/index.rst">}}>`_ post I have lined out some ideas for a next generation design that
`HSM basics <{{<ref "blog/hsm-basics/index.rst">}}>`_ post I have lined out some ideas for a next generation design that
deviates from the bread-and-butter apporoach of using a mesh as the primary security feature. Before embarking on
practical experiments with these ideas, I want to first take a stab at replicating the current state of the art as best
I can. State of the art meshes often use exotic substrates such as 3D plastic parts with traces chemically deposited on
@ -93,7 +97,7 @@ usable for this task.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/kicad-mesh-outline.png" alt="KiCAD showing an irregular board shape with rounded corners and
indents. In the middle of the board there is a footprint for a 4-pin surface-mount pin header.">
<figcaption>The process starts out with the mesh shape being defined inside KiCAD. The mesh's outline is drawn
@ -104,7 +108,7 @@ usable for this task.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/grid-vis-plain.svg" alt="A vizualization of the grid fitting process. Over the mesh's irregular
outline a grid is drawn. In this picture, all grid cells that are fully inside the grid are shown. Grid cells
that overlap the mesh border are highlighted. Grid cells outside of the mesh border are not drawn.">
@ -125,25 +129,29 @@ means that on every step, the algorithm is choosing a new direction at random.
.. raw:: html
<figure>
<figure class="side-by-side">
<img src="images/cells-0.svg" alt="a completely organized looking grid with spiral patterns all over.">
<figcaption>0%</figcaption>
</figure><figure class="side-by-side">
<img src="images/cells-25.svg">
<figcaption>25%</figcaption>
</figure><figure class="side-by-side">
<img src="images/cells-50.svg">
<figcaption>50%</figcaption>
</figure><figure class="side-by-side">
<img src="images/cells-75.svg">
<figcaption>75%</figcaption>
</figure><figure class="side-by-side">
<img src="images/cells-100.svg" alt="a completely random looking grid with cells aggregating into ireggular
areas that look like paint splotches.">
<figcaption>100%</figcaption>
</figure>
</figure>
<div class="subfigure" data-pagefind-ignore>
<figure>
<img src="images/cells-0.svg" alt="a completely organized looking grid with spiral patterns all over.">
<figcaption>0%</figcaption>
</figure>
<figure>
<img src="images/cells-25.svg">
<figcaption>25%</figcaption>
</figure>
<figure>
<img src="images/cells-50.svg">
<figcaption>50%</figcaption>
</figure>
<figure>
<img src="images/cells-75.svg">
<figcaption>75%</figcaption>
</figure>
<figure>
<img src="images/cells-100.svg" alt="a completely random looking grid with cells aggregating into ireggular
areas that look like paint splotches.">
<figcaption>100%</figcaption>
</figure>
</div>
After I have built this tree like you would do in a depth-first search, I draw my one or several mesh mesh traces into
it. The core observation here is that there is only 16 possible ways a cell can be connected: It has four neighbors,
@ -153,7 +161,7 @@ sides connected, a straight through, a 90 degree bend, and a "T"-junction—see
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/maze_tiles_plain.svg" style="max-width: 20em">
<figcaption>
There are six possible tile types in our connectivity graph inside its square tiling. This graphic illustrates
@ -165,7 +173,7 @@ After tiling the grid according to the key above, we get the result below.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/tiles-25-small.svg">
<figcaption>
An auto-routed mesh with traces colored according to tile types.
@ -174,7 +182,7 @@ After tiling the grid according to the key above, we get the result below.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/traces-25-small.svg">
<figcaption>
The same mesh, but with traces all black.
@ -185,7 +193,7 @@ Putting it all together got me the KiCAD plugin you can see in the screenshot be
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/kicad-mesh-settings2.png">
<figcaption>
The plugin settings window open.
@ -194,11 +202,11 @@ Putting it all together got me the KiCAD plugin you can see in the screenshot be
.. raw:: html
<figure>
<img src="images/kicad-mesh-result-large.png">
<figcaption>
After runing the plugin, the generated mesh looks like this in pcbnew.
</figcaption>
<figure data-pagefind-ignore>
<img src="images/kicad-mesh-result-large.png">
<figcaption>
After runing the plugin, the generated mesh looks like this in pcbnew.
</figcaption>
</figure>
I am fairly happy with the result, but getting there was a medium pain. Especially KiCAD's plugin API is still very
@ -214,7 +222,7 @@ making a copy of the board file first and treating mesh generation as a non-reve
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/grid-vis-plain.svg" alt="">
<figcaption></figcaption>
</figure>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View file

@ -0,0 +1,40 @@
---
title: "The KiCoil Planar Coil Generator"
date: 2025-12-31T13:15:39+02:00
summary: >
I wrote a layout tool generating planar coils that can handle spiral coils, toroidal coils, and hybrids in between
the two.
---
.. raw:: html
<figure data-pagefind-ignore>
<img src="header.png" style="max-width: 20em">
</figure>
A planar coil is a coil that is made from flat traces in some printing process like PCB or IC manufacturing, instead of
being wound from wire. A few weeks ago, I needed one such planar coil that
Project State
-------------
Currently, circular coils are special cased. Their layouts are directly generated, without the use of polygon
offsetting. Windings are efficiently approximated using circular arcs. The circular coil layout code is solid, and
contains decent (albeit not infallible) parameter sanity checks. Its main limitation is that sometimes, clearances can
be violated a bit.
The arbitrary shape code path is less stable, and produces faulty output in some cases. The most common error is
crossing traces near the first vertex of the polygon when the polygon has highly convex or concave parts. I'm still
improving this code path, but as long as you check the output, any errors it produces should be easy to fix by hand.
If you would like to contribute, I'd welcome any ideas on the arbitrary shape code path. I think there is no single
optimal solution here, and a generic algorithm that can be adjusted to favor for instance shape accuracy versus winding
smoothness would be nice.
All project links are listed on `https://jaseg.de/projects/kicoil/ <https://jaseg.de/projects/kicoil/>`__. You can check
out the code on my git at `https://git.jaseg.de/kicoil.git <https://git.jaseg.de/kicoil.git>`__. Issues are tracked on
codeberg at `https://codeberg.org/jaseg/kicoil <https://codeberg.org/jaseg/kicoil>`__. The kicad addon can be installed
from the KiCad plugin manager, and you can install the standalone kicoil python package `from PyPI
<https://pypi.org/project/kicoil/>`__.

View file

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 77 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 279 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 285 KiB

After

Width:  |  Height:  |  Size: 285 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 428 KiB

After

Width:  |  Height:  |  Size: 428 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 287 KiB

After

Width:  |  Height:  |  Size: 287 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 332 KiB

After

Width:  |  Height:  |  Size: 332 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 301 KiB

After

Width:  |  Height:  |  Size: 301 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 271 KiB

After

Width:  |  Height:  |  Size: 271 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 278 KiB

After

Width:  |  Height:  |  Size: 278 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 271 KiB

After

Width:  |  Height:  |  Size: 271 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 250 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 435 KiB

After

Width:  |  Height:  |  Size: 435 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 145 KiB

Before After
Before After

View file

@ -1,6 +1,12 @@
---
title: "LED Characterization"
date: 2018-05-02T11:18:38+02:00
summary: >
Recently, I have been working on a small driver for ambient lighting using 12V LED strips like you can get
inexpensively from China. I wanted to be able to just throw one of these somewhere, stick down some LED tape, hook
it up to a small transformer and be able to control it through Wifi. When I was writing the firmware, I noticed that
when fading between different colors, the colors look *all wrong*! This observation led me down a rabbit hole of
color perception and LED peculiarities.
---
Preface
@ -19,7 +25,7 @@ really important so you could dim it down a lot without flickering. I ended up u
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/rgb_cube.svg" alt="An illustration of the RGB color cube.">
<figcaption>An illustration of the RGB color cube.
<a href="https://commons.wikimedia.org/wiki/File:RGB_color_cube.svg">Picture</a> by
@ -46,7 +52,7 @@ transformation.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/hsv_cylinder.png" alt="An illustration of the HSV color space as a cylinder.">
<figcaption>An illustration of the HSV color space as a cylinder.
<a href="https://commons.wikimedia.org/wiki/File:HSV_color_solid_cylinder.png">Picture</a> by
@ -67,7 +73,7 @@ particular in the blues and greens we loose *a lot* of colors to sRGB.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<video controls loop>
<source src="video/sRGB.mkv" type="video/h264">
<source src="video/sRGB.webm" type="video/webm">
@ -98,19 +104,20 @@ over the problem, there are several sources for imperfections:
.. raw:: html
<figure>
<figure class="side-by-side">
<div class="subfigure" data-pagefind-ignore>
<figure>
<img src="images/driver_ringing_strong.jpg" alt="Strong ringing on the LED voltage waveform edge at about
100% overshoot during about 70% of the cycle time.">
<figcaption>The LED strip being at the end of a couple meters of wire caused extremely bad ringing at high
driving frequencies.</figcaption>
</figure><figure class="side-by-side">
</figure>
<figure>
<img src="images/driver_ringing_weak.jpg" alt="Weak ringing on the LED voltage waveform edge at about 30%
overshoot during about 20% of the cycle time.">
<figcaption>Adding a resistor in front of the MOSFET gate to slow the transition dampened the ringing
somewhat, but ultimately it cannot be eliminated entirely.</figcaption>
</figure>
</figure>
</div>
These last two errors are tricky to compensate. What I needed for that was basically a model of the *perceived* colors
of the LED tape's color channels. A way of doing his is to record the spectra of all color channels and then evaluate
@ -159,25 +166,27 @@ spectrum of the phosphor.
.. raw:: html
<figure>
<figure class="side-by-side">
<div class="subfigure" data-pagefind-ignore>
<figure>
<img src="images/spectrograph_step1_parts.jpg">
<figcaption>The ingredients. The cup of coffee and Madoka Magica DVD set are essential to the eventual
function of the appartus.</figcaption>
</figure><figure class="side-by-side">
</figure>
<figure>
<img src="images/spectrograph_step2.jpg">
<figcaption>Step 1: Cut to size and mark down all holes as described in <a
href="http://www.candac.ca/candacweb/sites/default/files/BuildaSpectroscope.pdf">the manual</a></figcaption>
</figure>
<figure class="side-by-side">
<figure>
<img src="images/spectrograph_step3.jpg">
<figcaption>Step 2: Cut out all holes</figcaption>
</figure><figure class="side-by-side">
</figure>
<figure>
<img src="images/spectrograph_step4_complete.jpg">
<figcaption>The finished result with the back side showing. The viewing window is on the bottom of the other
side.</figcaption>
</figure>
</figure>
</div>
Now that I had a spectrograph, I needed a somewhat predictable way of measuring the spectrum it gave me.
@ -192,7 +201,7 @@ different colors on their pictures. Some other sensor was needed.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/daylight_spectrum_dvd.jpg">
<figcaption>The daylight spectrum as seen using a DVD as a grating.
<a href="https://commons.wikimedia.org/wiki/File:SpectresSolaires-DVD.jpg">Picture</a> by
@ -232,7 +241,7 @@ trimpot on the virtual ground both amplifers use as a reference so I could trim
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/preamp_schematic.jpg" alt="A drawing of the photodiode preamplifier's schematic">
<figcaption>The photodiode preamplifier schematic. Schematic drawn with an unlicensed copy of
DaveCAD.</figcaption>
@ -252,15 +261,16 @@ here.
.. raw:: html
<figure>
<figure class="side-by-side">
<div class="subfigure" data-pagefind-ignore>
<figure>
<img src="images/preamp_front.jpg">
<figcaption>The front side of the preamplifier board.</figcaption>
</figure><figure class="side-by-side">
</figure>
<figure>
<img src="images/preamp_back.jpg">
<figcaption>The wiring of the photodiode preamp.</figcaption>
</figure>
</figure>
</div>
Given a way to measure intensity what remains missing is a way to scan a single photodiode across the spectrum.
@ -286,7 +296,7 @@ The whole unit with photodiode preamplifier, linear stage, photodiode and steppe
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/electronics_whole.jpg" alt="The complete electronics setup of the spectrograph. In the back
there is the DVD drive stepper stage. In front of it, mounted on a piece of wood are a small USB-to-12V
switching-regulator module to power the stepper motor in the top left, below on the bottom left is the
@ -334,7 +344,7 @@ begin.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/raw_plot_cheap_rgb.svg" alt="A plot with three wide peaks, two large peaks on both sides and
one smaller one in the middle. The middle one overlaps the two on the sides. The large ones are about 2.5V in
amplitude. Overall, the plot is about 300 stepper steps wide with each peak being around 130 steps wide.">
@ -371,7 +381,7 @@ of the blue peak towards red.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/photodiode_sensitivity.svg" alt="A plot of photodiode sensitivity against wavelength relative
to peak sensitivity at 820nm. The sensitivity rises from 20% at 380nm approximately linearly to 80% at 620nm,
then the rise rolls off.">
@ -390,7 +400,7 @@ wavelength in nanometers.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/processed_plot_cheap_rgb.svg" alt="A plot with three wide peaks, all three of different
heights. The leftmost peak is highest at 6nA, the middle peak lowest at 1.6nA and the rightmost peak in between
at 4nA. The middle one overlaps the two on the sides. Overall, the plot spans about 300nm on its x axis with
@ -424,7 +434,7 @@ models can be found in the `project repo`_.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<video controls loop>
<source src="video/led_within_srgb_scale=1.0.mkv" type="video/h264">
<source src="video/led_within_srgb_scale=1.0.webm" type="video/webm">
@ -479,9 +489,9 @@ can view the Jupyter notebook most of the analysis above `here <http://nbviewer.
.. _`on Github`: https://github.com/jaseg/esp_led_drv/blob/master/user/led_controller.c
.. _`project repo`: https://github.com/jaseg/led_drv
.. _`Wifi LED driver`: {{<ref "posts/wifi-led-driver/index.rst">}}
.. _`small driver`: {{<ref "posts/wifi-led-driver/index.rst">}}
.. _`multichannel LED driver`: {{<ref "posts/multichannel-led-driver/index.rst">}}
.. _`Wifi LED driver`: {{<ref "blog/wifi-led-driver/index.rst">}}
.. _`small driver`: {{<ref "blog/wifi-led-driver/index.rst">}}
.. _`multichannel LED driver`: {{<ref "blog/multichannel-led-driver/index.rst">}}
.. _`sRGB`: https://en.wikipedia.org/wiki/SRGB
.. _`CC BY-SA 3.0`: https://creativecommons.org/licenses/by-sa/3.0
.. _`Color spaces`: https://en.wikipedia.org/wiki/Color_space

View file

@ -0,0 +1,26 @@
---
title: "How to make cgit serve PDF files as direct downloads"
date: 2025-11-17T23:42:00+01:00
summary: >
cgit is great, but by default when you click on a PDF file in a repository content listing it will show you a
hexdump of the file. You can access the actual file by clicking the "plain" link on top of the listing, but that's
not only annoying, for large PDF files rendering the hexdump can also hang browser tabs.
---
cgit is great, but by default when you click on a PDF file in a repository content listing it will show you a
hexdump of the file. You can access the actual file by clicking the "plain" link on top of the listing, but that's
not only annoying, for large PDF files rendering the hexdump can also hang browser tabs.
I found a quick and easy solution to this problem, which I'm documenting here because it seems nobody on the
internet has really done this before, and the usual AI assistants (ChatGPT and Claude) are both deeply confused.
You just add a simple rewrite rule to your nginx config that 302-redirects requests to ``/tree/.../foobar.pdf`` to
``/plain/.../foobar.pdf``. Here's the rule, make sure you put them in your nginx config *before* the location directive
proxying requests to cgit.
.. code:: nginx
location ~ ^/([^/]+)/tree/(.*\.pdf)$ {
return 302 /$1/plain/$2;
}

View file

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 279 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 285 KiB

After

Width:  |  Height:  |  Size: 285 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 354 KiB

After

Width:  |  Height:  |  Size: 354 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 820 KiB

After

Width:  |  Height:  |  Size: 820 KiB

Before After
Before After

View file

@ -1,6 +1,10 @@
---
title: "32-Channel LED tape driver"
date: 2018-05-02T11:31:14+02:00
summary: >
Together, a friend and I outfitted the small staircase at Berlin's Chaos Computer Club with nice, shiny RGB-WW LED
tape for ambient lighting. For this installation, I made a 32-channel LED driver that achieves high dynamic range on
all 32 channels using a cheap microcontroller by using Binary Code Modulation.
---
Theoretical basics
@ -40,7 +44,7 @@ good timing.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/pwm_schema.jpg" alt="A visualization of PWM at different duty cycles.">
<figcaption>Waveforms of two PWM cycles at different duty cycles.</figcaption>
</figure>
@ -96,7 +100,7 @@ on.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/bcm_schema.jpg" alt="A visualization of BCM at different duty cycles.">
<figcaption>Waveforms of a single 4-bit BCM cycle at different duty cycles. This BCM can produce 16 different
levels.</figcaption>
@ -144,7 +148,7 @@ period, one pulse will reset the shift register and one will strobe the freshly-
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/olsndot_output_schematic.jpg" alt="From left to right, we see the STM32, one of the shift
registers, and the LEDs and MOSFETs. The LED tape is driven to ground by the MOSFETs, which are in turn directly
driven from the shift register outputs. The shift register is wired up to the STM32 with its clock and data
@ -199,7 +203,7 @@ voltrage we saw on our oscilloscope on the LED tape.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/driver_ringing_strong.jpg" alt="Strong ringing on the LED voltage waveform edge at about
100% overshoot during about 70% of the cycle time.">
<figcaption>Bad ringing on the LED output voltage caused by wiring inductance. Note that the effect on the
@ -215,7 +219,7 @@ likely culprit. The figure below is the schematic used for the simulations.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/driver_output_ltspice_schematic.jpg" alt="The LTSpice schematic of one output of the driver,
taking into account the shift register's output ESR and the wiring ESL.">
<figcaption>The schematic of the simulation in LTSpice</figcaption>
@ -228,7 +232,7 @@ driver.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/overshoot_sim_r0.svg" alt="The result of the LTSpice simulation of our driver output. The LED
current shows similar ringing to what we measured using the oscilloscope. Interestingly, the gate voltage shows
strong ringing, too.">
@ -246,7 +250,7 @@ since the estimated ESL and stray capacitance of the wiring is probably way off.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/driver_ringing_weak.jpg" alt="Weak ringing on the LED voltage waveform edge at about 30%
overshoot during about 20% of the cycle time.">
<figcaption>Adding a resistor in front of the MOSFET gate to slow the transition damped the ringing somewhat,
@ -257,7 +261,7 @@ since the estimated ESL and stray capacitance of the wiring is probably way off.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/overshoot_sim_r100.svg" alt="The result of the LTSpice simulation of our driver output with an
extra 100 Ohms between shift register output and MOSFET gate. Similar to the oscilloscope measurement the
ringing is much reduced in its amplitude.">
@ -273,7 +277,7 @@ cycle of the LED current is not at all equal to the 50% duty cycle of the excita
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/asymmetric_iled.svg" alt="The result of an LTSpice simulation of the LED duty cycle without and
with damping. Dampening widens the LED current waveform from 50% duty cycle with sharp edges to about 80% duty
cycle with soft edges.">
@ -283,7 +287,7 @@ cycle of the LED current is not at all equal to the 50% duty cycle of the excita
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/asymmetric_vgate.svg" alt="The gate voltages in the spice simulation above. The undamped
response shows sharp edges with the miller plateau being a barely noticeable step, but with strong ringing on
the trailing edge. The damped response shows RC-like slow-edges, but has wide miller plateaus on both edges
@ -327,7 +331,7 @@ sensitive owing to their physically large die area.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/linearization_setup.jpg" alt="The led measurement setup consists of several PCBs and a
breadboard linked with a bunch of wires and a big tin can to shield the LEDs and the photodiode. A large sub-D
connector is put into the top of the tin can as a feed-through for the LED tape's control signals and the
@ -356,7 +360,7 @@ photocurrents for a certain BCM setpoint just as our retinas would do.
.. raw:: html
<figure>
<figure data-pagefind-ignore>
<img src="images/driver_linearity_raw.svg" alt="">
<figcaption>
A plot of the measured brightness of our LED tape for each BCM period. The brightness values are normalized
@ -376,15 +380,16 @@ the resulting brightness curve below.
.. raw:: html
<figure>
<figure class="side-by-side">
<div class="subfigure" data-pagefind-ignore>
<figure>
<img src="images/uncorrected_brightness_sim.svg" alt="">
<figcaption>
Calculated brightness curve for the uncorrected BCM setup. As you can see, at low setpoints the result
is about as smooth as sandpaper, which is well in line with our observations. At high setpoints the
offset gets swamped out and the nonlinearity in the low bits is not visible anymore.
</figcaption>
</figure><figure class="side-by-side">
</figure>
<figure>
<img src="images/corrected_brightness_sim.svg" alt="">
<figcaption>
Brightness curve for the corrected BCM setup extrapolated using actual measurements. Looks as buttery
@ -392,7 +397,7 @@ the resulting brightness curve below.
</figcaption>
</figcaption>
</figure>
</figure>
</div>
.. _BPW34: http://www.vishay.com/docs/81521/bpw34.pdf
.. _MCP6002: http://ww1.microchip.com/downloads/en/DeviceDoc/21733j.pdf
@ -431,19 +436,20 @@ Conclusion
.. raw:: html
<figure>
<figure class="side-by-side">
<div class="subfigure" data-pagefind-ignore>
<figure>
<a href="images/olsndot_schematic.png">
<img src="images/olsndot_schematic.png" alt="A picture of the LED driver schematic">
</a>
<figcaption>The LED driver <a href="images/olsndot_schematic.png">schematic</a></figcaption>
</figure><figure class="side-by-side">
</figure>
<figure>
<a href="images/olsndot_pcb.png">
<img src="images/olsndot_pcb.png" alt="A picture of the LED driver PCB layout">
</a>
<figcaption>The LED driver <a href="images/olsndot_pcb.png">PCB layout</a></figcaption>
</figure>
</figure>
</div>
Putting some thought into the control circuitry and software, you can easily control large numbers of channels of LEDs
using extremely inexpensive driving hardware without any compromises on dynamic range. The design we settled on can

View file

@ -0,0 +1,18 @@
---
title: "Housekeeping note: git.jaseg.de has moved"
date: 2026-05-30T10:00:00+02:00
summary: >
A small note: As part of moving the servers and website to a new, more suitable host for my Ashen and Yanartas
projects as well as the creation of yasec, my freelance consulting business, I've moved git.jaseg.de from a custom
cgit/gitolite setup to a forgejo instance. This may have broken some links, especially deep links into the old cgit.
If you notice any broken links, please reach out through email.
---
A small note: As part of moving the servers and website to a new, more suitable host for my Ashen_ and Yanartas_
projects as well as the creation of yasec_, my freelance consulting business, I've moved `git.jaseg.de
<https://git.jaseg.de/>`__ from a custom cgit/gitolite setup to a forgejo instance. This may have broken some links,
especially deep links into the old cgit. If you notice any broken links, please reach out through email.
.. _Ashen: https://yasec.de/projects/ashen/
.. _Yanartas: https://yasec.de/projects/yanartas/
.. _yasec: https://yasec.de/

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

View file

@ -0,0 +1,104 @@
---
title: "New paper: Monitoring Tamper-Sensing Meshes Using Low-Cost, Embedded Time-Domain Reflectometry"
date: 2025-10-20T23:42:00+01:00
summary: >
I've got a new paper accepted at CHES, to be published in TCHES 2026/1 around beginning of December and out `on
eprint now <https://eprint.iacr.org/2025/1962>`__. The topic of the paper is a way of monitoring a tamper-sensing
mesh through time-domain reflectometry using very cheap components. The end result is a circuit that costs about
10 € in parts that is able to measure TDR responses with a few hundred picoseconds of resolution.
---
.. raw:: html
<figure data-pagefind-ignore>
<img src="pic_board_setup_2_small.jpg" alt="A PCB with several chips sitting on a table with another PCB
with only traces on it plugged in through a board-edge connector. The first PCB looks not very complex.">
<figcaption>
The final setup. On the right is the measurement board, and on the left is the mesh test specimen plugged
in. In a real application, you would integrate both into your target circuit.
</figcaption>
</figure>
I've got a new paper accepted at CHES, to be published in TCHES 2026/1 around beginning of December and out
`on eprint now <https://eprint.iacr.org/2025/1962>`__. The topic of the paper is a way of monitoring a tamper-sensing
mesh through time-domain reflectometry using very cheap components. The end result is a circuit that costs about 10 € in
parts that is able to measure TDR responses with a few hundred picoseconds of resolution.
Tamper-Sensing meshes are squiggly circuit traces that are used to tamper-proof high-security devices like hardware
security modules, ATM pin pads and countertop card payment terminals. Any area where you would like to prevent an
attacker from drilling or sawing through in a physical attack, you completely cover with one or more such circuit traces
in a meandering pattern. I've written up some work on a KiCad plugin for creating these meshes `in another post
<{{< ref "blog/kicad-mesh-plugin" >}}>`__.
Up to now, the state of the art in monitoring these security meshes has mostly been finding ways to precisely monitor
their ohmic resistance in the analog domain. This has the disadvantage of both being fairly complex in circuitry and of
presenting a steep trade-off between sensitivity and false-positive rate since all you get out of the whole mesh is a
single analog measurement containing maybe 12 to 16 bits of entropy. There have been a few papers on using more advanced
RF techniques, but they all either required really expensive circuitry and/or highly customized meshes that for instance
couldn't easily be fitted into arbitrary shapes.
.. raw:: html
<figure data-pagefind-ignore>
<img src="fig_edge_risetime.png" alt="Four plots showing edge response for four different chips: 74LVC2G157,
MAX3748, TDP0604 and PI3HDX12211. The first two are fairly slow at about 1 ns risetime, while the last two are
very fast at around 300 ps risetime.">
<figcaption>
The sampling edges as measured by the board itself. As you can see, using a cheap microcontroller and some
cheap display signal redriver ICs along with commodity RF schottkies you can get pretty spicy edges on a
budget. <a href="fig_edge_risetime.pdf">Link to full resolution.</a>
</figcaption>
</figure>
In this paper, I wrote up a method using the high-resolution timer of an inexpensive `STM32G4-series microcontroller
<https://www.st.com/resource/en/datasheet/stm32g474cb.pdf>`__ together with a DisplayPort/HDMI "redriver" chips meant for
amplifying high-speed display signals to create fast pulse edges. I characterized several chips, with the best
performers being TI's `TDP0604 <https://www.ti.com/product/TDP0604>`__ and Diodes' `PI3HDX12211
<https://www.diodes.com/part/view/PI3HDX12211>`__, coming in at 2 to 5 € depending on where and how much you buy. The
fast edges generated by these drivers are then fed to a set of four-diode sampling gates using cheap RF schottky diodes
to create a really cheap but fast time-domain reflectometer. Using this TDRD circuit, a security mesh can be monitored
much more precisely than before, since the circuit creates a sort of fingerprint of the mesh's trace along its length.
.. raw:: html
<div class="subfigure" data-pagefind-ignore>
<figure>
<img src="pic_74lvc_small.jpg" alt="">
<figcaption>
74LVC2G157
</figcaption>
</figure>
<figure>
<img src="pic_max3748_small.jpg" alt="">
<figcaption>
MAX3748
</figcaption>
</figure>
<figure>
<img src="pic_tdp0604_small.jpg" alt="">
<figcaption>
TDP0604
</figcaption>
</figure>
<figure>
<img src="pic_pi3hdx_small.jpg" alt="">
<figcaption>
PI3HDX12211
</figcaption>
</figure>
</div>
One of the fun highlights of this project to me was micro-soldering test boards using different redriver ICs. Above, you
can see the result of that soldering work. I was really happy with my cheap aliexpress microscope and with my fancy
titanium tweezers!
Have a look into the paper, where I wrote up details on the circuitry as well as a whole bunch of (>1000!) measurements
characterizing the system. As it turns out, it's really sensitive to attacks while being reasonably robust to
environmental disturbances. In fact, it's so sensitive that the circuit can distinguish multiple identical (!) copies of
the same mesh produces by JLCPCB from their manufacturing tolerances such as FR-4 fiber weave alignment.
You can find a preprint of the paper `on eprint <https://eprint.iacr.org/2025/1962>`__, and I'll update this post with a
link to the published version of the paper when it becomes available. The eprint is identical to the published version
as of now.
The source code of the project is available at `https://git.jaseg.de/sampling-mesh-monitor.git <https://git.jaseg.de/sampling-mesh-monitor.git>`__.

Some files were not shown because too many files have changed in this diff Show more