switch to new theme
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
output/
|
output/
|
||||||
|
venv/
|
||||||
|
@ -14,10 +14,9 @@ In order to use sidenotes and lightboxes, add `filters.py` as custom jinja filte
|
|||||||
from sys import path
|
from sys import path
|
||||||
from os import curdir
|
from os import curdir
|
||||||
path.append(curdir)
|
path.append(curdir)
|
||||||
from filters import add_lightboxes, add_sidenotes
|
from filters import add_sidenotes
|
||||||
|
|
||||||
JINJA_FILTERS = {'add_lightboxes': add_lightboxes,
|
JINJA_FILTERS = {'add_sidenotes': add_sidenotes}
|
||||||
'add_sidenotes': add_sidenotes}
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@ -2,17 +2,13 @@ Title: git commit --vandalism
|
|||||||
Date: 2023-05-25 18:55
|
Date: 2023-05-25 18:55
|
||||||
Author: Error
|
Author: Error
|
||||||
Slug: commit-vandalism
|
Slug: commit-vandalism
|
||||||
Summary: SHA-1 has been broken for some time, but is still used in git. To find out how git behaves when it encounters such a hash collision, I vandalized its source code and caused some collisions myself.
|
Summary: Vandalizing git to cause some hash collisions and find out how it behaves in that case
|
||||||
License: CC-BY-NC
|
License: CC-BY-NC
|
||||||
https://creativecommons.org/licenses/by-nc/4.0/
|
https://creativecommons.org/licenses/by-nc/4.0/
|
||||||
|
|
||||||
## Disclaimer
|
_This is not a vulnerability report.
|
||||||
|
|
||||||
This is not a vulnerability report.
|
|
||||||
I intentionally disabled safeguards in the code in order to be able observe this behavior.
|
I intentionally disabled safeguards in the code in order to be able observe this behavior.
|
||||||
However, it's a nice example for why you shouldn't use SHA-1 for anything security related and a good opportunity to learn more about the inner workings of git.
|
However, it's a nice example for why you shouldn't use SHA-1 for anything security related and a good opportunity to learn more about the inner workings of git._
|
||||||
|
|
||||||
## SHAttering git
|
|
||||||
|
|
||||||
In 2017, the Cryptology Group at Centrum Wiskunde & Informatica (CWI) and the Google Research Security, Privacy and Anti-abuse Group announced the first (public) SHA-1 hash collision.
|
In 2017, the Cryptology Group at Centrum Wiskunde & Informatica (CWI) and the Google Research Security, Privacy and Anti-abuse Group announced the first (public) SHA-1 hash collision.
|
||||||
They generated 2 PDF files with the same hash in an attack they called SHAttered.
|
They generated 2 PDF files with the same hash in an attack they called SHAttered.
|
||||||
@ -27,6 +23,7 @@ Let's try it out.
|
|||||||
So if the SHAttered files already have a hash collision, they could be used to cause a collision in git, right?
|
So if the SHAttered files already have a hash collision, they could be used to cause a collision in git, right?
|
||||||
Let's put them in a repository and find out.
|
Let's put them in a repository and find out.
|
||||||
|
|
||||||
|
:::bash
|
||||||
$ wget https://shattered.io/static/shattered-1.pdf -O shattered1.pdf -q
|
$ wget https://shattered.io/static/shattered-1.pdf -O shattered1.pdf -q
|
||||||
$ sha1sum shattered-1.pdf
|
$ sha1sum shattered-1.pdf
|
||||||
38762cf7f55934b34d179ae6a4c80cadccbb7f0a shattered-1.pdf
|
38762cf7f55934b34d179ae6a4c80cadccbb7f0a shattered-1.pdf
|
||||||
@ -39,6 +36,7 @@ Let's put them in a repository and find out.
|
|||||||
As promised, these files have different contents, but the same SHA-1 hash.
|
As promised, these files have different contents, but the same SHA-1 hash.
|
||||||
The next step is to add them to a git repository and observe what happens.
|
The next step is to add them to a git repository and observe what happens.
|
||||||
|
|
||||||
|
:::bash
|
||||||
$ cp shattered-1.pdf shattered.pdf
|
$ cp shattered-1.pdf shattered.pdf
|
||||||
$ git add shattered.pdf
|
$ git add shattered.pdf
|
||||||
$ git commit -m 'shattered'
|
$ git commit -m 'shattered'
|
||||||
@ -57,19 +55,19 @@ Such a collision did not happen.
|
|||||||
The file was updated successfully in the second commit and checking out the first commit restores the original version.
|
The file was updated successfully in the second commit and checking out the first commit restores the original version.
|
||||||
Investigating this closer shows that git somehow calculates different hashes for these files.
|
Investigating this closer shows that git somehow calculates different hashes for these files.
|
||||||
|
|
||||||
|
:::bash
|
||||||
$ git hash-object shattered-1.pdf
|
$ git hash-object shattered-1.pdf
|
||||||
b621eeccd5c7edac9b7dcba35a8d5afd075e24f2
|
b621eeccd5c7edac9b7dcba35a8d5afd075e24f2
|
||||||
$ git hash-object shattered-2.pdf
|
$ git hash-object shattered-2.pdf
|
||||||
ba9aaa145ccd24ef760cf31c74d8f7ca1a2e47b0
|
ba9aaa145ccd24ef760cf31c74d8f7ca1a2e47b0
|
||||||
|
|
||||||
## Objects and IDs in git
|
|
||||||
|
|
||||||
Internally, git is a key-value store.
|
Internally, git is a key-value store.
|
||||||
It generates an ID for each object that it stores, which can later be used to identify and retrieve the object.
|
It generates an ID for each object that it stores, which can later be used to identify and retrieve the object.
|
||||||
By default, this ID is a SHA-1 hash.
|
By default, this ID is a SHA-1 hash.
|
||||||
The input given to the hash function depends on the object type.
|
The input given to the hash function depends on the object type.
|
||||||
For files, which are stored as so called blobs, the ID is generated by hashing the string `blob`, the length of the file, a null byte and the file itself. [^3]
|
For files, which are stored as so called blobs, the ID is generated by hashing the string `blob`, the length of the file, a null byte and the file itself. [^3]
|
||||||
|
|
||||||
|
:::bash
|
||||||
$ echo "so long and thanks for all the fish" > test.txt
|
$ echo "so long and thanks for all the fish" > test.txt
|
||||||
$ git hash-object test.txt
|
$ git hash-object test.txt
|
||||||
8b86cb67f1f19db567a100b55edb5466a33e7fb7
|
8b86cb67f1f19db567a100b55edb5466a33e7fb7
|
||||||
@ -80,6 +78,7 @@ Blobs can be assigned names to using tree objects.
|
|||||||
These objects can group multiple blobs and trees, which are referenced by their id. [^3]
|
These objects can group multiple blobs and trees, which are referenced by their id. [^3]
|
||||||
A tree object could look like this for example:
|
A tree object could look like this for example:
|
||||||
|
|
||||||
|
:::bash
|
||||||
$ git cat-file -p 8004c8a7b6fce1452539556bb4c4c91b92b5c2bc
|
$ git cat-file -p 8004c8a7b6fce1452539556bb4c4c91b92b5c2bc
|
||||||
100644 blob ba9aaa145ccd24ef760cf31c74d8f7ca1a2e47b0 shattered.pdf
|
100644 blob ba9aaa145ccd24ef760cf31c74d8f7ca1a2e47b0 shattered.pdf
|
||||||
|
|
||||||
@ -90,6 +89,7 @@ Commits are just another object type in git.
|
|||||||
These objects associate a tree with an author and committer,[°The author and the committer can be different. The author is who wrote the code and the committer is who added it to the repository.] a parent commit (if it exists) and a commit message to describe to changes included in this commit. [^3]
|
These objects associate a tree with an author and committer,[°The author and the committer can be different. The author is who wrote the code and the committer is who added it to the repository.] a parent commit (if it exists) and a commit message to describe to changes included in this commit. [^3]
|
||||||
The resulting object can once again be viewed with `git cat-file`.
|
The resulting object can once again be viewed with `git cat-file`.
|
||||||
|
|
||||||
|
:::bash
|
||||||
$ git cat-file -p f1906a7bf1c4c8ce49f264e2f3ba313c305b7ede
|
$ git cat-file -p f1906a7bf1c4c8ce49f264e2f3ba313c305b7ede
|
||||||
tree 8004c8a7b6fce1452539556bb4c4c91b92b5c2bc
|
tree 8004c8a7b6fce1452539556bb4c4c91b92b5c2bc
|
||||||
parent 0aa8c5ad0be568d680d5f613807bde46a39424e3
|
parent 0aa8c5ad0be568d680d5f613807bde46a39424e3
|
||||||
@ -100,10 +100,6 @@ The resulting object can once again be viewed with `git cat-file`.
|
|||||||
|
|
||||||
The commit ID is generated by hashing this information, prepended by the same header that is used for the other object types: `$object-type $length\0`.[°You can find a nice example how the commit hash is constructed [here](https://gist.github.com/masak/2415865 "How is git commit sha1 formed").]
|
The commit ID is generated by hashing this information, prepended by the same header that is used for the other object types: `$object-type $length\0`.[°You can find a nice example how the commit hash is constructed [here](https://gist.github.com/masak/2415865 "How is git commit sha1 formed").]
|
||||||
|
|
||||||
## Causing Collisions
|
|
||||||
|
|
||||||
### Vandalism
|
|
||||||
|
|
||||||
OK, so that's the reason why `git hash-object` returns different values for the two SHAttered files.[°Try it yourself if you want: prepending any data to these files will make their SHA-1 hashes differ. `echo test | cat - shatterd-1.pdf | sha1sum` results in a different hash than `echo test | cat - shatterd-2.pdf | sha1sum`]
|
OK, so that's the reason why `git hash-object` returns different values for the two SHAttered files.[°Try it yourself if you want: prepending any data to these files will make their SHA-1 hashes differ. `echo test | cat - shatterd-1.pdf | sha1sum` results in a different hash than `echo test | cat - shatterd-2.pdf | sha1sum`]
|
||||||
Further, this explains why the commit hashes didn't collide.
|
Further, this explains why the commit hashes didn't collide.
|
||||||
There won't be any progress made by using the existing SHA-1 hash collision from SHAttered, but collisions can still happen.
|
There won't be any progress made by using the existing SHA-1 hash collision from SHAttered, but collisions can still happen.
|
||||||
@ -115,6 +111,7 @@ Git can be built with multiple different SHA-1 backends.
|
|||||||
By default, it uses an implementation with a collision attack detection mechanism.[°This collision attack detection is not relevant for our experiment for now. We'll get back to it later.]
|
By default, it uses an implementation with a collision attack detection mechanism.[°This collision attack detection is not relevant for our experiment for now. We'll get back to it later.]
|
||||||
It can be found in the `SHA1DC` directory.
|
It can be found in the `SHA1DC` directory.
|
||||||
|
|
||||||
|
:::C
|
||||||
int SHA1DCFinal(unsigned char output[20], SHA1_CTX *ctx)
|
int SHA1DCFinal(unsigned char output[20], SHA1_CTX *ctx)
|
||||||
{
|
{
|
||||||
uint32_t last = ctx->total & 63;
|
uint32_t last = ctx->total & 63;
|
||||||
@ -163,10 +160,9 @@ This reduces the effective length of the identifier to 1 byte.
|
|||||||
We're not looking for a specific hash, any collision will do.
|
We're not looking for a specific hash, any collision will do.
|
||||||
The first collisions should occur after a few tries.[°Such collisions are already very likely after a surprisingly small amount of attempts if the value range is not too large. Look up the birthday problem if you don't know it.]
|
The first collisions should occur after a few tries.[°Such collisions are already very likely after a surprisingly small amount of attempts if the value range is not too large. Look up the birthday problem if you don't know it.]
|
||||||
|
|
||||||
### Test Set Up
|
|
||||||
|
|
||||||
So let's compile git with our "improvement" and try to cause some collisions.
|
So let's compile git with our "improvement" and try to cause some collisions.
|
||||||
|
|
||||||
|
:::bash
|
||||||
$ touch test
|
$ touch test
|
||||||
$ ../git/git add test
|
$ ../git/git add test
|
||||||
$ ../git/git commit -m 'test'
|
$ ../git/git commit -m 'test'
|
||||||
@ -196,6 +192,7 @@ The fist two commits may not collide, but it's already obvious what our little m
|
|||||||
|
|
||||||
Continuing to add and commits to this repository, the first collision was observed on the 7th attempt.
|
Continuing to add and commits to this repository, the first collision was observed on the 7th attempt.
|
||||||
|
|
||||||
|
:::bash
|
||||||
$ touch test7
|
$ touch test7
|
||||||
$ ../git/git add test7
|
$ ../git/git add test7
|
||||||
$ ../git/git commit -m 'test7'
|
$ ../git/git commit -m 'test7'
|
||||||
@ -215,6 +212,7 @@ It looks like the attempt to add a new object failed silently.
|
|||||||
|
|
||||||
On the 15th attempt, another interesting collision occurred:
|
On the 15th attempt, another interesting collision occurred:
|
||||||
|
|
||||||
|
:::bash
|
||||||
$ touch test15
|
$ touch test15
|
||||||
$ ../git/git add test15
|
$ ../git/git add test15
|
||||||
$ ../git/git commit -m 'test15'
|
$ ../git/git commit -m 'test15'
|
||||||
@ -246,8 +244,6 @@ After the creation of the new commit object once again failed silently, git proc
|
|||||||
This confirms the assumption that git will keep the existing object in case of a collision.
|
This confirms the assumption that git will keep the existing object in case of a collision.
|
||||||
The other commits that got rolled back are still present as objects in git and can be checked out, but especially if the user does not notice what happened here and continues working, this can seriously mess up the commit history.
|
The other commits that got rolled back are still present as objects in git and can be checked out, but especially if the user does not notice what happened here and continues working, this can seriously mess up the commit history.
|
||||||
|
|
||||||
### Observations
|
|
||||||
|
|
||||||
Those tests were continued until the following collisions occurred and git's behavior could be observed:
|
Those tests were continued until the following collisions occurred and git's behavior could be observed:
|
||||||
|
|
||||||
Collisions between two blobs
|
Collisions between two blobs
|
||||||
@ -287,7 +283,7 @@ Collisions between commits and tags
|
|||||||
: Creating the new commit object fails. The existing tag object remains unchanged. Git attempts to check out the new commit (that wasn't created) and fails with the error message `fatal: cannot update ref 'refs/heads/main': trying to write non-commit object $HASH to branch 'refs/heads/main'`.
|
: Creating the new commit object fails. The existing tag object remains unchanged. Git attempts to check out the new commit (that wasn't created) and fails with the error message `fatal: cannot update ref 'refs/heads/main': trying to write non-commit object $HASH to branch 'refs/heads/main'`.
|
||||||
|
|
||||||
Collisions between tags and blobs
|
Collisions between tags and blobs
|
||||||
: A new file is created under `.git/refs/tags` pointing at the hash, but the creation of the new tag object under `.git/objects` fails silently. The existing blob object remains unchanged. <!--TODO-->
|
: A new file is created under `.git/refs/tags` pointing at the hash, but the creation of the new tag object under `.git/objects` fails silently. The existing blob object remains unchanged.
|
||||||
|
|
||||||
Collisions between tags and trees
|
Collisions between tags and trees
|
||||||
: A new file is created under `.git/refs/tags` pointing at the hash, but the creation of the new tag object under `.git/objects` fails silently. The existing tree object remains unchanged. The tag is displayed by `git tag -l`, but attempts at checking it out fail with the error message `fatal: Cannot switch branch to a non-commit`.
|
: A new file is created under `.git/refs/tags` pointing at the hash, but the creation of the new tag object under `.git/objects` fails silently. The existing tree object remains unchanged. The tag is displayed by `git tag -l`, but attempts at checking it out fail with the error message `fatal: Cannot switch branch to a non-commit`.
|
||||||
@ -298,16 +294,12 @@ Collisions between tags and commits
|
|||||||
Collisions between two tags
|
Collisions between two tags
|
||||||
: A new file is created under `.git/refs/tags` pointing at the hash, but the creation of this new tag object under `.git/objects` fails silently. The existing tag object remains unchanged. Since the new tag reference object points at the old tag object, the new tag will be an alias for the old tag, with the same message and object reference.
|
: A new file is created under `.git/refs/tags` pointing at the hash, but the creation of this new tag object under `.git/objects` fails silently. The existing tag object remains unchanged. Since the new tag reference object points at the old tag object, the new tag will be an alias for the old tag, with the same message and object reference.
|
||||||
|
|
||||||
### Summary
|
|
||||||
|
|
||||||
Git does not overwrite existing objects.
|
Git does not overwrite existing objects.
|
||||||
Creating a new object with a hash that's already associated with another object always fails silently.
|
Creating a new object with a hash that's already associated with another object always fails silently.
|
||||||
However, depending on the object type, git shows some interesting behavior.
|
However, depending on the object type, git shows some interesting behavior.
|
||||||
If the object type fits, git will just continue with it as if nothing is wrong, e.g. it will commit an old tree or checkout an old commit.
|
If the object type fits, git will just continue with it as if nothing is wrong, e.g. it will commit an old tree or checkout an old commit.
|
||||||
Git only responds with an error if the object types are incompatible, e.g. if the collision causes a reference to a tree to point at a tag object instead.
|
Git only responds with an error if the object types are incompatible, e.g. if the collision causes a reference to a tree to point at a tag object instead.
|
||||||
|
|
||||||
## Mitigation
|
|
||||||
|
|
||||||
Hash collisions, intentional oŕ not, can become a problem for git.
|
Hash collisions, intentional oŕ not, can become a problem for git.
|
||||||
To mitigate this risk, git includes a mechanism that detects SHA-1 collision attacks and reacts by hashing the suspected block 3 times, extending SHA-1 from 80 to 240 steps in these cases.
|
To mitigate this risk, git includes a mechanism that detects SHA-1 collision attacks and reacts by hashing the suspected block 3 times, extending SHA-1 from 80 to 240 steps in these cases.
|
||||||
This ensures that different hashes are generated in theses cases.[^4]
|
This ensures that different hashes are generated in theses cases.[^4]
|
||||||
@ -316,8 +308,6 @@ Further, git does not only support SHA-1.
|
|||||||
It supports SHA-256, too.
|
It supports SHA-256, too.
|
||||||
Unlike SHA-1, SHA-256 is considered cryptographically secure.
|
Unlike SHA-1, SHA-256 is considered cryptographically secure.
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
Actual SHA-1 collisions are very unlikely to occur as a coincidence.
|
Actual SHA-1 collisions are very unlikely to occur as a coincidence.
|
||||||
The collisions in this experiment could only be observed after the code was modified to limit the effective hash size to 1 byte.
|
The collisions in this experiment could only be observed after the code was modified to limit the effective hash size to 1 byte.
|
||||||
Otherwise, it would not have been possible to create collisions with the available resources.
|
Otherwise, it would not have been possible to create collisions with the available resources.
|
@ -2,12 +2,10 @@ Title: RFCartography - Visualizing relations between RFCs
|
|||||||
Date: 2023-02-28 19:50
|
Date: 2023-02-28 19:50
|
||||||
Author: Error
|
Author: Error
|
||||||
Slug: rfcartography
|
Slug: rfcartography
|
||||||
Summary: RFCs make up a nice set of structured data with many relations. I built a small(-ish) website that analyzes these relationships and visualizes them as graphs.
|
Summary: Analyzing relationships between RFCs and drawing graphs of them
|
||||||
License: CC-BY-NC
|
License: CC-BY-NC
|
||||||
https://creativecommons.org/licenses/by-nc/4.0/
|
https://creativecommons.org/licenses/by-nc/4.0/
|
||||||
|
|
||||||
## Setting Sails
|
|
||||||
|
|
||||||
RFCs are nice documents which explain how the internet works.
|
RFCs are nice documents which explain how the internet works.
|
||||||
They define protocols, describe best practices and discuss the organisation of internet infrastructure.
|
They define protocols, describe best practices and discuss the organisation of internet infrastructure.
|
||||||
Unlike some standards from other organizations, they are publicly available for free, usually written in a comprehensible manner and structured in a logical way.
|
Unlike some standards from other organizations, they are publicly available for free, usually written in a comprehensible manner and structured in a logical way.
|
||||||
@ -17,7 +15,7 @@ I always liked the way RFCs are connected to each other:
|
|||||||
Information about which RFC updates which, which RFC is obsoleted by which, and so on, all displayed at the top of the document, easy to find.
|
Information about which RFC updates which, which RFC is obsoleted by which, and so on, all displayed at the top of the document, easy to find.
|
||||||
So I decided to create RFCartography[°portmanteau word built out of "RFC" and "Cartography"; If you were under the impression that it has something to do with radio frequencies, you were misled, sorry.], a small tool that draws graphs of these relations.
|
So I decided to create RFCartography[°portmanteau word built out of "RFC" and "Cartography"; If you were under the impression that it has something to do with radio frequencies, you were misled, sorry.], a small tool that draws graphs of these relations.
|
||||||
|
|
||||||
![A graph of the relations of RFC791 (Internet Protocol) generated by RFCartography]({static}/posts/rfcartography/791.svg "A graph of the relations of RFC791 (Internet Protocol) generated by RFCartography")
|
![A graph of the relations of RFC791 (Internet Protocol) generated by RFCartography]({static}/blog/rfcartography/791.svg "A graph of the relations of RFC791 (Internet Protocol) generated by RFCartography")
|
||||||
|
|
||||||
Parsing all the RFCs in order to extract the required information would have been a lot of effort and probably prone to errors.
|
Parsing all the RFCs in order to extract the required information would have been a lot of effort and probably prone to errors.
|
||||||
Fortunately, [rfc-editor.org](https://rfc-editor.org) has a nice, machine readable RFC index file in XML format with all the meta data for each RFC. [^1]
|
Fortunately, [rfc-editor.org](https://rfc-editor.org) has a nice, machine readable RFC index file in XML format with all the meta data for each RFC. [^1]
|
||||||
@ -41,14 +39,13 @@ is-also
|
|||||||
see-also
|
see-also
|
||||||
: references other relevant documents [^2]
|
: references other relevant documents [^2]
|
||||||
|
|
||||||
## How the Cartographer works
|
|
||||||
|
|
||||||
RFCartography consists out of the IndexParser component, which parses the index file and makes its information available, and the RFCartography component, which builds the requested graphs and generates SVGs from them.
|
RFCartography consists out of the IndexParser component, which parses the index file and makes its information available, and the RFCartography component, which builds the requested graphs and generates SVGs from them.
|
||||||
This is held together by a Flask application that initially calls the IndexParser and provides the data returned by the parser to the RFCartographer, which it then repeatedly consults to generate graphs and render SVGs.
|
This is held together by a Flask application that initially calls the IndexParser and provides the data returned by the parser to the RFCartographer, which it then repeatedly consults to generate graphs and render SVGs.
|
||||||
Further, the Flask application implements the web frontend.
|
Further, the Flask application implements the web frontend.
|
||||||
The application is made available by a web server and a wsgi server.
|
The application is made available by a web server and a wsgi server.
|
||||||
|
|
||||||
![Component diagram of RFCartography]({static}/posts/rfcartography/components.svg "Component diagram of RFCartography")
|
![Component diagram of RFCartography]({static}/blog/rfcartography/components.svg "Component diagram of RFCartography")
|
||||||
|
|
||||||
When a request is received, the Flask application calls the RFCartographer to generate the subgraph belonging to the requested RFC.
|
When a request is received, the Flask application calls the RFCartographer to generate the subgraph belonging to the requested RFC.
|
||||||
In order to do this, the initial RFC is added to a queue.
|
In order to do this, the initial RFC is added to a queue.
|
||||||
@ -80,9 +77,7 @@ Once the queue is empty, the subgraph generation is completed and the graph can
|
|||||||
|
|
||||||
Afterwards, the subgraph can be rendered into a SVG with the help of pyplot.
|
Afterwards, the subgraph can be rendered into a SVG with the help of pyplot.
|
||||||
|
|
||||||
## Here be Dragons
|
Of course this does not work without problems.
|
||||||
|
|
||||||
Of course it does not work without problems.
|
|
||||||
First of all, RFCartography is really slow.
|
First of all, RFCartography is really slow.
|
||||||
Generating and rendering graphs takes a while, leading to long response times.
|
Generating and rendering graphs takes a while, leading to long response times.
|
||||||
This can be mitigated to a degree by caching responses, but to really improve on this issue, the application should probably use a database backend.[°I'll add this to RFCartography at some point]
|
This can be mitigated to a degree by caching responses, but to really improve on this issue, the application should probably use a database backend.[°I'll add this to RFCartography at some point]
|
||||||
@ -93,9 +88,7 @@ However, this does not work in some edge cases.
|
|||||||
If, for example, a subgraph consists out of only one node, pyplot does not add links to it.
|
If, for example, a subgraph consists out of only one node, pyplot does not add links to it.
|
||||||
Further, it does not add links to the nodes if different shapes are used for different node types.[°Using different shapes would have been nice from an accessibility point of view, but since it doesn't work at the moment, RFCartography has to rely on colors only.]
|
Further, it does not add links to the nodes if different shapes are used for different node types.[°Using different shapes would have been nice from an accessibility point of view, but since it doesn't work at the moment, RFCartography has to rely on colors only.]
|
||||||
|
|
||||||
![A graph of the relations of RFC2322 (Management of IP numbers by peg-dhcp) generated by RFCartography]({static}/posts/rfcartography/2322.svg "A graph of the relations of RFC2322 (Management of IP numbers by peg-dhcp) generated by RFCartography")
|
![A graph of the relations of RFC2322 (Management of IP numbers by peg-dhcp) generated by RFCartography]({static}/blog/rfcartography/2322.svg "A graph of the relations of RFC2322 (Management of IP numbers by peg-dhcp) generated by RFCartography")
|
||||||
|
|
||||||
## Draw your own Maps
|
|
||||||
|
|
||||||
You can try RFCartography yourself on [rfcartography.net](https://rfcartography.net/ "RFCartography").
|
You can try RFCartography yourself on [rfcartography.net](https://rfcartography.net/ "RFCartography").
|
||||||
The source code can be found [here](https://git.undefinedbehavior.de/undef/RFCartography "RFCartography - undefined git server").
|
The source code can be found [here](https://git.undefinedbehavior.de/undef/RFCartography "RFCartography - undefined git server").
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
@ -2,12 +2,10 @@ Title: Slot Car Racing over IP (SCRoIP)
|
|||||||
Date: 2022-08-28 16:05
|
Date: 2022-08-28 16:05
|
||||||
Author: Error
|
Author: Error
|
||||||
Slug: scroip
|
Slug: scroip
|
||||||
Summary: I built a remote control for a slot car racing track to play with it over the LAN. It's unnecessary complicated and inefficient, but at least it supports IPv6. Ladies, Gentlemen and everyone else, start your engines.
|
Summary: A remote control for a slot car racing tracks; Unnecessarily complicated and inefficient; Ladies, Gentlemen and everyone else, start your engines.
|
||||||
License: CC-BY-NC
|
License: CC-BY-NC
|
||||||
https://creativecommons.org/licenses/by-nc/4.0/
|
https://creativecommons.org/licenses/by-nc/4.0/
|
||||||
|
|
||||||
## The Idea
|
|
||||||
|
|
||||||
When I was a child, I used to play a lot with my slot car racing track.
|
When I was a child, I used to play a lot with my slot car racing track.
|
||||||
After stumbling upon it again recently, I started wondering if I could build something to control it over the network.
|
After stumbling upon it again recently, I started wondering if I could build something to control it over the network.
|
||||||
Not that I needed a remote control for it, but it seemed like a fun project.
|
Not that I needed a remote control for it, but it seemed like a fun project.
|
||||||
@ -15,11 +13,9 @@ However, simply creating an API that offers precise control over the device seem
|
|||||||
So I came up with the idea to control it with the network traffic itself.
|
So I came up with the idea to control it with the network traffic itself.
|
||||||
The more traffic is sent to the remote control, the faster the car is supposed to go.[°Use more bandwidth!!!]
|
The more traffic is sent to the remote control, the faster the car is supposed to go.[°Use more bandwidth!!!]
|
||||||
|
|
||||||
## Reversing
|
|
||||||
|
|
||||||
In order to build the remote control, the first step was to figure out how the slot car racing system works. So I disassembled it and took a look at its wiring.
|
In order to build the remote control, the first step was to figure out how the slot car racing system works. So I disassembled it and took a look at its wiring.
|
||||||
|
|
||||||
![Simplified circuit diagram for a slot car racing system]({static}/posts/scroip/carrera.svg "Simplified circuit diagram of a slot car racing system")
|
![Simplified circuit diagram for a slot car racing system]({static}/blog/scroip/carrera.svg "Simplified circuit diagram of a slot car racing system")
|
||||||
|
|
||||||
The underlying mechanism is fairly simple. The power supply provides about 14.8 V DC voltage, which is used to power the car's engine.
|
The underlying mechanism is fairly simple. The power supply provides about 14.8 V DC voltage, which is used to power the car's engine.
|
||||||
The speed of the car is controlled by a variable resistor within the controller, which allows to control the voltage the car receives.
|
The speed of the car is controlled by a variable resistor within the controller, which allows to control the voltage the car receives.
|
||||||
@ -27,19 +23,15 @@ Pushing the controller further down lowers the resistance, which raises the volt
|
|||||||
A third wire is used to brake the car when the controller is in its neutral position by utilizing the current generated by the motor when the car is rolling out.
|
A third wire is used to brake the car when the controller is in its neutral position by utilizing the current generated by the motor when the car is rolling out.
|
||||||
A look at wikipedia confirms this analysis. [^1]
|
A look at wikipedia confirms this analysis. [^1]
|
||||||
|
|
||||||
## Building a digital Controller
|
|
||||||
|
|
||||||
Now that I understood the mechanism, the next step was to create a digital version of it, so that I could control the slot car racing track with a Raspberry Pi.
|
Now that I understood the mechanism, the next step was to create a digital version of it, so that I could control the slot car racing track with a Raspberry Pi.
|
||||||
My first idea was to simply use a digital potentiometer.
|
My first idea was to simply use a digital potentiometer.
|
||||||
However, searching for a suitable digipot, I quickly realized that there were no options with the desired dimensions of about 30 Ω available.
|
However, searching for a suitable digipot, I quickly realized that there were no options with the desired dimensions of about 30 Ω available.
|
||||||
Therefore I built it myself using a few resistors, solid state relais and multiplexers.[°I'm sure there are simpler solutions for this, but, well, it worked for me.]
|
Therefore I built it myself using a few resistors, solid state relais and multiplexers.[°I'm sure there are simpler solutions for this, but, well, it worked for me.]
|
||||||
|
|
||||||
![SCRoIP controller circuit diagram]({static}/posts/scroip/scroip.svg "SCRoIP controller circuit diagram")
|
![SCRoIP controller circuit diagram]({static}/blog/scroip/scroip.svg "SCRoIP controller circuit diagram")
|
||||||
|
|
||||||
For the sake of simplicity,[°or maybe out of laziness, who knows? :)] I did not add the braking mechanism from the analog controller to my digital version.
|
For the sake of simplicity,[°or maybe out of laziness, who knows? :)] I did not add the braking mechanism from the analog controller to my digital version.
|
||||||
|
|
||||||
## Implementing the Server
|
|
||||||
|
|
||||||
With the hardware fully assembled and soldered, it was time to have a look at the software.
|
With the hardware fully assembled and soldered, it was time to have a look at the software.
|
||||||
Python offers an easy way of implementing TCP servers.[°This project will use TCP. Even though the payload is immediately discarded, reliability is of the utmost importance and packet loss will not be accepted!!!]
|
Python offers an easy way of implementing TCP servers.[°This project will use TCP. Even though the payload is immediately discarded, reliability is of the utmost importance and packet loss will not be accepted!!!]
|
||||||
By default, it only supports IPv4, but IPv6 support can be added by setting `address_family` to `AF_INET6`.
|
By default, it only supports IPv4, but IPv6 support can be added by setting `address_family` to `AF_INET6`.
|
||||||
@ -70,17 +62,15 @@ Once every 100 ms, it then calculates the average throughput for that time perio
|
|||||||
|
|
||||||
The full server code can be found [here](https://git.undefinedbehavior.de/undef/SCRoIP "Slot Car Racing over IP - SCRoIP - undefined git server").
|
The full server code can be found [here](https://git.undefinedbehavior.de/undef/SCRoIP "Slot Car Racing over IP - SCRoIP - undefined git server").
|
||||||
|
|
||||||
## Lights out and away we go
|
|
||||||
|
|
||||||
Once the server was implemented as well, all that was left to do was to test it.
|
Once the server was implemented as well, all that was left to do was to test it.
|
||||||
So I deployed the server script to a Raspberry Pi, connected its GPIO pins to the controller and the controller to the slot car racing track.
|
So I deployed the server script to a Raspberry Pi, connected its GPIO pins to the controller and the controller to the slot car racing track.
|
||||||
Using netcat, I was now able to accelerate the car with the network traffic I generated:
|
Using netcat, I was now able to accelerate the car with the network traffic I generated:
|
||||||
|
|
||||||
<video controls preload="metadata">
|
<video controls preload="metadata">
|
||||||
<source src="{static}/posts/scroip/demo.webm" type="video/webm">
|
<source src="{static}/blog/scroip/demo.webm" type="video/webm">
|
||||||
<p>
|
<p>
|
||||||
The video could not be loaded.
|
The video could not be loaded.
|
||||||
You can try to download it directly <a href="/posts/scroip/demo.webm">here</a> instead.
|
You can try to download it directly <a href="/blog/scroip/demo.webm">here</a> instead.
|
||||||
</p>
|
</p>
|
||||||
</video>
|
</video>
|
||||||
|
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 214 KiB |
@ -2,158 +2,59 @@
|
|||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
sodipodi:docname="favicon.svg"
|
width="120"
|
||||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
height="120"
|
||||||
sodipodi:version="0.32"
|
viewBox="0 0 120 120"
|
||||||
id="svg2"
|
|
||||||
height="100.00000pt"
|
|
||||||
width="100.00000pt"
|
|
||||||
version="1.1"
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
sodipodi:docname="favicon.svg"
|
||||||
|
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
||||||
<defs
|
|
||||||
id="defs3">
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="Arrow2Mend"
|
|
||||||
orient="auto"
|
|
||||||
refY="0"
|
|
||||||
refX="0"
|
|
||||||
id="Arrow2Mend"
|
|
||||||
style="overflow:visible">
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
id="path2928"
|
|
||||||
style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
|
|
||||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
|
||||||
transform="matrix(-0.6,0,0,-0.6,3,0)" />
|
|
||||||
</marker>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="Arrow2Lend"
|
|
||||||
orient="auto"
|
|
||||||
refY="0.0"
|
|
||||||
refX="0.0"
|
|
||||||
id="Arrow2Lend"
|
|
||||||
style="overflow:visible;">
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
id="path2931"
|
|
||||||
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
|
|
||||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
|
||||||
transform="scale(1.1) rotate(180) translate(-5,0)" />
|
|
||||||
</marker>
|
|
||||||
<marker
|
|
||||||
inkscape:stockid="Arrow2Lstart"
|
|
||||||
orient="auto"
|
|
||||||
refY="0.0"
|
|
||||||
refX="0.0"
|
|
||||||
id="Arrow2Lstart"
|
|
||||||
style="overflow:visible">
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
id="path2922"
|
|
||||||
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round"
|
|
||||||
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
|
|
||||||
transform="scale(1.1) translate(-5,0)" />
|
|
||||||
</marker>
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
<sodipodi:namedview
|
||||||
inkscape:window-y="0"
|
id="namedview7"
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-height="1019"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:grid-points="true"
|
|
||||||
showgrid="true"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
inkscape:document-units="pt"
|
|
||||||
inkscape:cy="72.389693"
|
|
||||||
inkscape:cx="66.290084"
|
|
||||||
inkscape:zoom="4.3445412"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
borderopacity="1.0"
|
|
||||||
bordercolor="#666666"
|
|
||||||
pagecolor="#ffffff"
|
pagecolor="#ffffff"
|
||||||
id="base"
|
bordercolor="#000000"
|
||||||
inkscape:showpageshadow="0"
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
inkscape:pagecheckerboard="0"
|
inkscape:pagecheckerboard="0"
|
||||||
inkscape:deskcolor="#d1d1d1"
|
inkscape:deskcolor="#d1d1d1"
|
||||||
showguides="false"
|
inkscape:document-units="px"
|
||||||
inkscape:window-maximized="1">
|
showgrid="true"
|
||||||
|
inkscape:zoom="8.5583333"
|
||||||
|
inkscape:cx="38.7926"
|
||||||
|
inkscape:cy="60.233691"
|
||||||
|
inkscape:window-width="3840"
|
||||||
|
inkscape:window-height="2057"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="text790">
|
||||||
<inkscape:grid
|
<inkscape:grid
|
||||||
id="GridFromPre046Settings"
|
|
||||||
type="xygrid"
|
type="xygrid"
|
||||||
originx="0"
|
id="grid1887" />
|
||||||
originy="0"
|
|
||||||
spacingx="6.6666667"
|
|
||||||
spacingy="6.6666667"
|
|
||||||
color="#3f3fff"
|
|
||||||
empcolor="#3f3fff"
|
|
||||||
opacity="0.15"
|
|
||||||
empopacity="0.38"
|
|
||||||
empspacing="5" />
|
|
||||||
</sodipodi:namedview>
|
</sodipodi:namedview>
|
||||||
<metadata
|
<defs
|
||||||
id="metadata4">
|
id="defs2" />
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<cc:license
|
|
||||||
rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" />
|
|
||||||
</cc:Work>
|
|
||||||
<cc:License
|
|
||||||
rdf:about="http://creativecommons.org/publicdomain/zero/1.0/">
|
|
||||||
<cc:permits
|
|
||||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
|
||||||
<cc:permits
|
|
||||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
|
||||||
<cc:permits
|
|
||||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
|
||||||
</cc:License>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
<g
|
||||||
id="layer1"
|
inkscape:label="Layer 1"
|
||||||
inkscape:groupmode="layer"
|
inkscape:groupmode="layer"
|
||||||
inkscape:label="Layer 1">
|
id="layer1"
|
||||||
<ellipse
|
style="display:inline">
|
||||||
id="path1291"
|
<g
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:4.37372;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
aria-label=":3"
|
||||||
cx="72.325401"
|
transform="scale(1.0709353,0.93376322)"
|
||||||
cy="66.657509"
|
id="text790"
|
||||||
rx="58.419399"
|
style="font-size:128.272px;font-family:Ubuntu;-inkscape-font-specification:'Ubuntu, Normal';fill:#ff0080;stroke-width:2.27832">
|
||||||
ry="58.453125" />
|
|
||||||
<path
|
<path
|
||||||
sodipodi:nodetypes="cc"
|
d="m 29.665878,89.846378 q 0,3.46335 -2.308896,6.02879 -2.308896,2.56544 -6.285329,2.56544 -3.976432,0 -6.285328,-2.56544 -2.308896,-2.56544 -2.308896,-6.02879 0,-3.46334 2.308896,-6.02878 2.308896,-2.56544 6.285328,-2.56544 3.976433,0 6.285329,2.56544 2.308896,2.56544 2.308896,6.02878 z m 0,-51.180525 q 0,3.463344 -2.308896,6.028784 -2.308896,2.56544 -6.285329,2.56544 -3.976432,0 -6.285328,-2.56544 -2.308896,-2.56544 -2.308896,-6.028784 0,-3.463344 2.308896,-6.028784 2.308896,-2.56544 6.285328,-2.56544 3.976433,0 6.285329,2.56544 2.308896,2.56544 2.308896,6.028784 z"
|
||||||
id="path2052"
|
id="path2307" />
|
||||||
d="M 42.608225,36.93892 V 96.373349"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:3.9623;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<path
|
<path
|
||||||
sodipodi:nodetypes="cc"
|
d="m 65.838621,100.04401 q 11.416208,0 16.290544,-4.489521 5.002608,-4.48952 5.002608,-12.057568 0,-4.874336 -2.052352,-8.209408 Q 83.027069,71.95244 79.691997,70.02836 76.356925,67.976008 71.867405,67.206376 67.506157,66.308472 62.888364,66.308472 H 59.809836 V 56.5598 h 4.232976 q 3.206801,0 6.541873,-0.64136 3.463344,-0.64136 6.285328,-2.308896 2.821984,-1.667536 4.617792,-4.48952 1.795808,-2.950256 1.795808,-7.311504 0,-3.591616 -1.28272,-6.157056 -1.28272,-2.56544 -3.463344,-4.232977 -2.052352,-1.667536 -4.874336,-2.308896 -2.693712,-0.769632 -5.77224,-0.769632 -6.157057,0 -10.518305,1.795808 -4.232976,1.795808 -7.183232,3.719889 l -4.617792,-9.107313 q 1.539264,-1.026176 3.84816,-2.180624 2.437168,-1.28272 5.387424,-2.308896 2.950256,-1.026176 6.285328,-1.667536 3.463344,-0.769632 7.183233,-0.769632 7.05496,0 12.18584,1.795808 5.13088,1.667536 8.465952,4.874336 3.335072,3.078528 5.002608,7.439776 1.667536,4.232977 1.667536,9.235585 0,6.926688 -3.976432,11.929296 -3.976432,4.874336 -9.6204,7.439776 3.463344,1.026176 6.670144,2.950256 3.2068,1.92408 5.515696,4.874336 2.437168,2.950256 3.84816,6.798417 1.539264,3.84816 1.539264,8.594224 0,5.77224 -2.052352,10.774848 -1.92408,4.874336 -6.157056,8.465947 -4.104704,3.59162 -10.390032,5.64397 -6.285328,2.05235 -14.879552,2.05235 -3.335073,0 -6.926689,-0.51309 -3.463344,-0.51308 -6.541872,-1.28272 -3.078528,-0.76963 -5.387424,-1.53926 -2.308896,-0.76963 -3.335072,-1.28272 l 2.308896,-10.390029 q 2.180624,1.026176 7.183232,2.693712 5.13088,1.667537 12.442385,1.667537 z"
|
||||||
id="path2054"
|
id="path2309" />
|
||||||
d="M -16.826204,66.656135 H 42.608225"
|
</g>
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:3.9623;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="ccc"
|
|
||||||
id="path2056"
|
|
||||||
d="M 42.608225,56.750396 92.136916,17.127444 v -39.622951"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:3.9623;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-mid:none" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="ccc"
|
|
||||||
id="path2058"
|
|
||||||
d="M 42.608225,76.561873 67.372571,96.373349 92.136916,116.18483"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:3.9623;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-mid:url(#Arrow2Mend)" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cc"
|
|
||||||
id="path9839"
|
|
||||||
d="M 92.136916,155.80778 V 116.18483"
|
|
||||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:3.9623;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 3.7 KiB |
17
content/pages/about.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
Title: About
|
||||||
|
Date: 2023-10-21 20:22
|
||||||
|
Author: Error
|
||||||
|
Status: published
|
||||||
|
Summary: You encountered an error...
|
||||||
|
Hidemeta: True
|
||||||
|
|
||||||
|
## Hi, I'm Error.
|
||||||
|
|
||||||
|
I'm a security engineer from Germany interested in networks, security, privacy and many other topics.
|
||||||
|
I enjoy spending time in hackspaces and can often be found at CCC events.
|
||||||
|
|
||||||
|
On this blog, I'll write about some projects of mine.
|
||||||
|
Some of them might be more usefull, some of them less.
|
||||||
|
In the end, most of them are the result of me being curious about something and simply trying it out.
|
||||||
|
|
||||||
|
Feel free to look around.
|
@ -1,12 +1,15 @@
|
|||||||
Title: Contact
|
Title: Contact
|
||||||
Date: 2023-03-25 16:45
|
Date: 2023-10-21 20:32
|
||||||
Author: Error
|
Author: Error
|
||||||
Status: published
|
Status: published
|
||||||
Summary: Information on how to contact me
|
Summary: Error messages
|
||||||
|
Hidemeta: True
|
||||||
|
|
||||||
## Mail
|
|
||||||
|
|
||||||
My mail address is `error[at]mail.undefinedbehavior.de`. If you are using PGP, please encrypt your mails to me. You can find my PGP key here:
|
You can find me on the fediverse as <a rel="me" href="https://chaos.social/@Error">@Error</a> on [chaos.social](https://chaos.social "chaos.social mastodon instance").
|
||||||
|
My mail address is `error[at]mail.undefinedbehavior.de`.
|
||||||
|
If you are using PGP, please encrypt your mails to me.
|
||||||
|
You can find my PGP key here:
|
||||||
|
|
||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
@ -44,6 +47,4 @@ My mail address is `error[at]mail.undefinedbehavior.de`. If you are using PGP, p
|
|||||||
=+HJ8
|
=+HJ8
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
## Fediverse
|
Feel free to say hello.
|
||||||
|
|
||||||
You can find me on [chaos.social](https://chaos.social "chaos.social mastodon instance") as <a rel="me" href="https://chaos.social/@Error">@Error</a>.
|
|
@ -1,17 +1,21 @@
|
|||||||
Title: Credits
|
Title: Credits
|
||||||
Date: 2022-08-28 16:05
|
Date: 2023-08-26 22:10
|
||||||
Author: Error
|
Author: Error
|
||||||
Status: hidden
|
Status: hidden
|
||||||
Summary: Credit where credit is due
|
Summary: Credit where credit is due
|
||||||
|
Hidemeta: True
|
||||||
|
|
||||||
## Pelican
|
## Pelican
|
||||||
|
|
||||||
This site is powered by [Pelican](https://getpelican.com "Pelican Static Site Generator, Powered by Python"). Pelican is a static site generator written in python.
|
This site is powered by [Pelican](https://getpelican.com "Pelican Static Site Generator, Powered by Python"). Pelican is a static site generator written in python.
|
||||||
It's licensed under the [GNU Affero General Public License v3.0](https://www.gnu.org/licenses/#AGPL "GNU Affero General Public License")
|
It's licensed under the [GNU Affero General Public License v3.0](https://www.gnu.org/licenses/#AGPL "GNU Affero General Public License").
|
||||||
|
|
||||||
## <span class="latex">L<span>A</span>T<span>E</span>X</span>.css
|
## Fonts
|
||||||
|
|
||||||
This site's template is based on [<span class="latex">L<span>A</span>T<span>E</span>X</span>.css](https://latex.now.sh "LaTeX.css"), a CSS library that makes a website look like a <span class="latex">L<span>A</span>T<span>E</span>X</span> document.
|
This website uses [Maven Pro](https://fonts.google.com/specimen/Maven+Pro) and [Oxygen Mono](https://fonts.google.com/specimen/Oxygen+Mono), both of which are available under the [SIL Open Font License (OFL)](https://scripts.sil.org/OFL).
|
||||||
<span class="latex">L<span>A</span>T<span>E</span>X</span>.css is licensed under the MIT License.
|
|
||||||
|
|
||||||
The template can be found [here](https://git.undefinedbehavior.de/undef/undefined-pelican-theme "Undefined Pelican Theme").
|
## reset.css
|
||||||
|
|
||||||
|
Like many other websites, this site relies on [reset.css](http://meyerweb.com/eric/tools/css/reset/) (public domain) to remove browser-side default CSS settings.
|
||||||
|
|
||||||
|
_Thank you very much._
|
||||||
|
@ -1,20 +1,16 @@
|
|||||||
Title: Lost and Found
|
Title: Lost and Found
|
||||||
Date: 2023-04-25 10:15
|
Date: 2023-10-21 20:40
|
||||||
Author: Error
|
Author: Error
|
||||||
Slug: found
|
Slug: found
|
||||||
Status: hidden
|
Status: hidden
|
||||||
Summary: Looks like you found something I may have lost and you want to return it to me. Thank you very much.
|
Summary: If you see this page, you probably found something I've lost
|
||||||
|
Hidemeta: True
|
||||||
|
|
||||||
## Thank you
|
You seem to have found something I've lost.
|
||||||
|
Please let me know what you found and where I can find it.
|
||||||
You seem to have found something I've lost and are trying to return it to me.
|
|
||||||
Thank you very much.
|
|
||||||
|
|
||||||
## How to contact me
|
|
||||||
|
|
||||||
Please let me know what you found and where it is.
|
|
||||||
|
|
||||||
If I'm at a chaos event, you can probably reach me via my DECT.
|
If I'm at a chaos event, you can probably reach me via my DECT.
|
||||||
Usually, my number is 7303.
|
Usually, my number is 7303.
|
||||||
|
|
||||||
Other options for contacting me are listed on my [contact page](/contact "Contact"), including e-mail and my fedi account.
|
Other options for contacting me are listed on my [contact page](/contact "Contact"), including e-mail and my fedi account.
|
||||||
|
|
||||||
|
_Thank you very much_
|
||||||
|
@ -2,11 +2,8 @@ Title: Imprint
|
|||||||
Date: 2022-08-28 16:05
|
Date: 2022-08-28 16:05
|
||||||
Author: Error
|
Author: Error
|
||||||
Status: hidden
|
Status: hidden
|
||||||
Summary: Legaly required contact information
|
Summary: Legally required contact information
|
||||||
|
Hidemeta: True
|
||||||
## Disclaimer
|
|
||||||
|
|
||||||
This information is only provided for legal reasons. Please reach out to me via [e-mail or fedi](/contact "Contact") instead. It's much faster and simpler.
|
|
||||||
|
|
||||||
## Verantwortlich gemäß §5 TMG
|
## Verantwortlich gemäß §5 TMG
|
||||||
|
|
||||||
@ -14,3 +11,7 @@ This information is only provided for legal reasons. Please reach out to me via
|
|||||||
Heilmannstraße 4b
|
Heilmannstraße 4b
|
||||||
70190 Stuttgart
|
70190 Stuttgart
|
||||||
Germany
|
Germany
|
||||||
|
|
||||||
|
This information is only provided for legal reasons.
|
||||||
|
Please reach out to me via [e-mail or fedi](/contact "Contact") instead.
|
||||||
|
It's much faster and simpler.
|
||||||
|
@ -3,6 +3,7 @@ Date: 2022-08-28 16:05
|
|||||||
Author: Error
|
Author: Error
|
||||||
Status: hidden
|
Status: hidden
|
||||||
Summary: Only necessary data is processed. No logging, no tracking, no analytics;
|
Summary: Only necessary data is processed. No logging, no tracking, no analytics;
|
||||||
|
Hidemeta: True
|
||||||
|
|
||||||
## Privacy Statement
|
## Privacy Statement
|
||||||
|
|
||||||
|
41
filters.py
@ -1,43 +1,6 @@
|
|||||||
def add_lightboxes(html: str,
|
|
||||||
lightbox_class: str = "lightbox",
|
|
||||||
wrapper_class: str = "lightbox-wrapper") -> str:
|
|
||||||
counter: int = 0
|
|
||||||
img_start: int = html.find("<img")
|
|
||||||
while img_start > -1:
|
|
||||||
counter += 1
|
|
||||||
img_end: int = html.find(">", img_start + 4)
|
|
||||||
lightbox: str = f"<span id=\"fig{counter}\"></span>\n" +\
|
|
||||||
f"<figure id=\"figure{counter}\">\n" +\
|
|
||||||
f" <a href=\"#figure{counter}\">\n" +\
|
|
||||||
f" {html[img_start:img_end + 1]}\n" +\
|
|
||||||
f" </a>\n"
|
|
||||||
alt_start: int = html.find("alt=\"", img_start + 4)
|
|
||||||
if alt_start > -1:
|
|
||||||
alt_end: int = html.find("\"", alt_start + 5)
|
|
||||||
lightbox = lightbox + f" <figcaption>\n" +\
|
|
||||||
f" {html[alt_start + 5:alt_end]}\n" +\
|
|
||||||
f" </figcaption>\n"
|
|
||||||
lightbox = lightbox + f" <div class=\"{lightbox_class}\">\n" +\
|
|
||||||
f" <div class=\"{wrapper_class}\">\n" +\
|
|
||||||
f" <a href=\"#fig{counter}\">\n" +\
|
|
||||||
f" [close]\n" +\
|
|
||||||
f" </a>\n" +\
|
|
||||||
f" {html[img_start:img_end + 1]}\n" +\
|
|
||||||
f" </div>\n" +\
|
|
||||||
f" </div>\n" +\
|
|
||||||
f"</figure>\n"
|
|
||||||
if html[img_start - 3:img_start] == "<p>" and html[img_end + 1:img_end + 5] == "</p>":
|
|
||||||
img_start -= 3
|
|
||||||
img_end += 4
|
|
||||||
html = html[:img_start] + lightbox + html[img_end + 1:]
|
|
||||||
img_start = html.find("<img", img_start + len(lightbox))
|
|
||||||
return html
|
|
||||||
|
|
||||||
|
|
||||||
def add_sidenotes(html: str,
|
def add_sidenotes(html: str,
|
||||||
toggle_class: str = "sidenote-toggle",
|
toggle_class: str = "sidenote-toggle",
|
||||||
label_class: str = "sidenote-number",
|
label_class: str = "sidenote-number") -> str:
|
||||||
sidenote_class: str = "sidenote") -> str:
|
|
||||||
counter: int = 0
|
counter: int = 0
|
||||||
start: int = html.find("[°")
|
start: int = html.find("[°")
|
||||||
while start > -1:
|
while start > -1:
|
||||||
@ -46,7 +9,7 @@ def add_sidenotes(html: str,
|
|||||||
text: str = html[start + 2:end]
|
text: str = html[start + 2:end]
|
||||||
sidenote: str = f"<label for=\"sn-{counter}\" class=\"{toggle_class} {label_class}\"></label>\n" +\
|
sidenote: str = f"<label for=\"sn-{counter}\" class=\"{toggle_class} {label_class}\"></label>\n" +\
|
||||||
f"<input type=\"checkbox\" id=\"sn-{counter}\" class=\"{toggle_class}\" />\n" +\
|
f"<input type=\"checkbox\" id=\"sn-{counter}\" class=\"{toggle_class}\" />\n" +\
|
||||||
f"<span class=\"{sidenote_class}\">\n" +\
|
f"<span class=\"sidenote\">\n" +\
|
||||||
f" {text}\n" +\
|
f" {text}\n" +\
|
||||||
f"</span>\n"
|
f"</span>\n"
|
||||||
html = html[:start] + sidenote + html[end + 1:]
|
html = html[:start] + sidenote + html[end + 1:]
|
||||||
|
@ -1,28 +1,38 @@
|
|||||||
from sys import path
|
from sys import path
|
||||||
from os import curdir
|
from os import curdir
|
||||||
path.append(curdir)
|
path.append(curdir)
|
||||||
from filters import add_lightboxes, add_sidenotes
|
from filters import add_sidenotes
|
||||||
|
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
DATE = str(date.today())
|
DATE = str(date.today())
|
||||||
|
|
||||||
AUTHOR = 'Error'
|
# General Settings
|
||||||
SITENAME = 'Undefined Behavior'
|
AUTHOR = 'error'
|
||||||
|
SITENAME = 'Undefined <br/>Behavior'
|
||||||
SITEURL = ''
|
SITEURL = ''
|
||||||
ABSTRACT = "If, as Adam Savage famously put it, the only difference between screwing around and science is writing it down, I should probably write about my projects somewhere. I'll just do it here."
|
PATH = 'content'
|
||||||
|
THEME = 'undefined-theme'
|
||||||
FAVICON = 'favicon.svg'
|
FAVICON = 'favicon.svg'
|
||||||
STATIC_PATHS = ['favicon.svg']
|
STATIC_PATHS = ['favicon.svg']
|
||||||
|
DISPLAY_PAGES_ON_MENU = True
|
||||||
|
|
||||||
TIMEZONE = 'Europe/Rome'
|
|
||||||
|
# Locale Settings
|
||||||
|
TIMEZONE = 'Europe/Berlin'
|
||||||
DEFAULT_DATE_FORMAT = '%Y-%m-%d'
|
DEFAULT_DATE_FORMAT = '%Y-%m-%d'
|
||||||
DEFAULT_LANG = 'en'
|
DEFAULT_LANG = 'en'
|
||||||
|
|
||||||
THEME = 'undefined-pelican-theme'
|
# Feed Generation Settings
|
||||||
PATH = 'content'
|
FEED_ALL_ATOM = 'feeds/atom.xml'
|
||||||
|
CATEGORY_FEED_ATOM = None
|
||||||
|
TRANSLATION_FEED_ATOM = None
|
||||||
|
AUTHOR_FEED_ATOM = None
|
||||||
|
AUTHOR_FEED_RSS = None
|
||||||
|
|
||||||
ARTICLE_URL = 'posts/{slug}/'
|
# Site Path Settings
|
||||||
ARTICLE_SAVE_AS = 'posts/{slug}/index.html'
|
ARTICLE_URL = 'blog/{slug}/'
|
||||||
|
ARTICLE_SAVE_AS = 'blog/{slug}/index.html'
|
||||||
PAGE_URL = '{slug}/'
|
PAGE_URL = '{slug}/'
|
||||||
PAGE_SAVE_AS = '{slug}/index.html'
|
PAGE_SAVE_AS = '{slug}/index.html'
|
||||||
AUTHOR_URL = ''
|
AUTHOR_URL = ''
|
||||||
@ -34,24 +44,22 @@ CATEGORIES_SAVE_AS = ''
|
|||||||
TAG_URL = ''
|
TAG_URL = ''
|
||||||
TAG_SAVE_AS = ''
|
TAG_SAVE_AS = ''
|
||||||
TAGS_SAVE_AS = ''
|
TAGS_SAVE_AS = ''
|
||||||
ARCHIVES_SAVE_AS = ''
|
ARCHIVES_SAVE_AS = 'blog/index.html'
|
||||||
|
|
||||||
# Feed generation settings
|
|
||||||
FEED_ALL_ATOM = 'feeds/atom.xml'
|
|
||||||
CATEGORY_FEED_ATOM = None
|
|
||||||
TRANSLATION_FEED_ATOM = None
|
|
||||||
AUTHOR_FEED_ATOM = None
|
|
||||||
AUTHOR_FEED_RSS = None
|
|
||||||
|
|
||||||
|
# Theme Specific Settings
|
||||||
LINKS = (('credits', '/credits'),
|
LINKS = (('credits', '/credits'),
|
||||||
('imprint', '/imprint'),
|
('imprint', '/imprint'),
|
||||||
('privacy', '/privacy'),)
|
('privacy', '/privacy'),)
|
||||||
|
DESCRIPTION = "If, as Adam Savage famously put it, the only difference between screwing around and science is writing it down, I should write about my projects somewhere."
|
||||||
|
|
||||||
|
# Social widget
|
||||||
|
SOCIAL = (('You can add links in your config file', '#'),
|
||||||
|
('Another social link', '#'),)
|
||||||
|
|
||||||
DEFAULT_PAGINATION = False
|
DEFAULT_PAGINATION = False
|
||||||
|
|
||||||
# dev settings
|
# Development Settings
|
||||||
RELATIVE_URLS = True
|
RELATIVE_URLS = True
|
||||||
DELETE_OUTPUT_DIRECTORY = True
|
DELETE_OUTPUT_DIRECTORY = True
|
||||||
|
|
||||||
JINJA_FILTERS = {'add_lightboxes': add_lightboxes,
|
JINJA_FILTERS = {'add_sidenotes': add_sidenotes}
|
||||||
'add_sidenotes': add_sidenotes}
|
|
||||||
|