Compare commits
2 Commits
6363b15c5a
...
4afa8a45f4
| Author | SHA1 | Date | |
|---|---|---|---|
| 4afa8a45f4 | |||
| 2fcc58a46f |
37
blog.html
37
blog.html
@@ -52,6 +52,23 @@
|
||||
<div class="row" style="gap: 2em">
|
||||
<!-- blog -->
|
||||
<div class="column" style="--custom_width: 33%; gap: 1em">
|
||||
<a href="blog/hshwd_release.html" class="card" style="text-decoration: none;">
|
||||
<img src="projects/hshwd/thumb.png
|
||||
" alt="Release of hshwd
|
||||
" title="Release of hshwd
|
||||
" class="card_image"/>
|
||||
<div class="box">
|
||||
<div class="column" style="width: 100%;">
|
||||
<h3 class="card_title">Release of hshwd
|
||||
</h3>
|
||||
<p class="card_text">
|
||||
hshwd is an open-source offline tool that generates strong, unique passwords from weak ones in a deterministic way, using the properties of hash functions.
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div><div class="column" style="--custom_width: 33%; gap: 1em">
|
||||
<a href="blog/gang_release.html" class="card" style="text-decoration: none;">
|
||||
<img src="projects/gang/thumb.png
|
||||
" alt="Release of Gang
|
||||
@@ -81,23 +98,6 @@
|
||||
<p class="card_text">
|
||||
If you’ve been following this blog since its beginnings in 2024, back when the URL was still ailyaut.robotfumeur.fr, then you’ve probably noticed that it hasn’t been updated much in the last year.
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div><div class="column" style="--custom_width: 33%; gap: 1em">
|
||||
<a href="blog/202405_moxxy_progress_report.html" class="card" style="text-decoration: none;">
|
||||
<img src="blog/media/2024/20240407_moxxy_progress.png
|
||||
" alt="Moxxy UI: May 2024 progress report
|
||||
" title="Moxxy UI: May 2024 progress report
|
||||
" class="card_image"/>
|
||||
<div class="box">
|
||||
<div class="column" style="width: 100%;">
|
||||
<h3 class="card_title">Moxxy UI: May 2024 progress report
|
||||
</h3>
|
||||
<p class="card_text">
|
||||
Let's start with a little announcement: from now on, I'll be publishing my .penpot file in the moxxy/design repository on Codeberg on a monthly basis, so that everyone can tinker with the interface to their heart's content! I should have done it from the start, but better late than never!
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -110,7 +110,8 @@
|
||||
<div class="box">
|
||||
<div class="column" style="width: 100%;">
|
||||
<h2 id="all">All posts</h2>
|
||||
<hr><h3>2025</h3><p class="light">23/11 <a href="blog/gang_release.html">Release of Gang
|
||||
<hr><h3>2026</h3><p class="light">05/02 <a href="blog/hshwd_release.html">Release of hshwd
|
||||
</a></p><hr><h3>2025</h3><p class="light">23/11 <a href="blog/gang_release.html">Release of Gang
|
||||
</a></p><p class="light">01/06 <a href="blog/a_new_dawn.html">A new dawn
|
||||
</a></p><hr><h3>2024</h3><p class="light">09/06 <a href="blog/202405_moxxy_progress_report.html">Moxxy UI: May 2024 progress report
|
||||
</a></p><p class="light">08/05 <a href="blog/202404_moxxy_progress_report.html">Moxxy UI: April 2024 progress report
|
||||
|
||||
147
blog/hshwd_release.html
Normal file
147
blog/hshwd_release.html
Normal file
@@ -0,0 +1,147 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Release of hshwd
|
||||
– Ailyaut's blog</title>
|
||||
<link rel="icon" type="image/png" href="../media/icons/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="stylesheet" href="../style.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta property="og:title" content="Release of hshwd
|
||||
– Ailyaut's blog" />
|
||||
<meta property="og:type" content="blog" />
|
||||
<meta property="og:url" content="https://ailyaut.com/" />
|
||||
<meta property="og:image" content="https://ailyaut.com/projects/hshwd/thumb.png
|
||||
" />
|
||||
<meta property="og:description" content="hshwd is an open-source offline tool that generates strong, unique passwords from weak ones in a deterministic way, using the properties of hash functions.
|
||||
" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div id="header_box">
|
||||
<div>
|
||||
<a href="../index.html" id="header_title">Ailyaut's blog</a>
|
||||
</div>
|
||||
<nav id="header_nav">
|
||||
<a href="../blog.html" class="nav_button_active">Blog</a>
|
||||
<a href="../gallery.html" class="nav_button">Gallery</a>
|
||||
<a href="../projects.html" class="nav_button">Projects</a>
|
||||
<a href="../about.html" class="nav_button">About</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<div class="box">
|
||||
<div class="column" style="--custom_width: 60%;">
|
||||
<h1>Release of hshwd
|
||||
</h1><p class="light">05/02/2026 · 8 min</p><div class="spacer" style="--size: 1em"></div>
|
||||
<p>Summary: hshwd is an open-source offline tool that generates strong, unique passwords from weak ones in a deterministic way, using the properties of hash functions.</p>
|
||||
<h2>Table of contents</h2>
|
||||
<p>
|
||||
<a href="#some-background">Some background</a><br>
|
||||
<a href="#good-bad-ugly">The Good, the Bad and the Ugly</a><br>
|
||||
<a href="#make-it-look-random">Make it look random</a><br>
|
||||
<a href="#make-it-slow">Make it slow</a><br>
|
||||
<a href="#make-it-unbreakable">Make it unbreakable</a><br>
|
||||
<a href="#usability-concerns">Usability concerns</a><br>
|
||||
<a href="#faq">FAQ</a><br>
|
||||
<a href="#downloads">Downloads</a><br>
|
||||
</p>
|
||||
<h2 id="some-background">Some background</h2>
|
||||
<p>Back in 2023, I made a small tool for generating many complex, unique passwords from simple ones. I called it pwgen, but in reality I could've called it SHA-1 because this is really all it did. <br>
|
||||
The intended use was that you'd type your usual password (you know, the one you use everywhere even though you shouldn't) and then you would append something unique (e.g. the name of the website you're creating a password for), and then the program would return the SHA-1 hash of your input and ta-da! You get a long, unique password that you can generate back every time you need it because hashing is deterministic.</p>
|
||||
<p>We're now in 2026 and I have just completed an <a target="_blank" href="https://www.coursera.org/learn/crypto">online course on cryptography</a> after realising that this topic has been fascinating me for years (thank you <a target="_blank" href="https://soatok.blog/">Soatok's blog</a>!).</p>
|
||||
<p>I am now the most knowledgeable I have ever been (and less than I'll be tomorrow), so now is the perfect time to aknowledge that pwgen had problems and to redo it properly*.</p>
|
||||
<p class="light">*that is, to the best of my current knowledge</p>
|
||||
<h2 id="good-bad-ugly">The Good, the Bad and the Ugly</h2>
|
||||
<p>While pwgen is good at creating many unique passwords, it is by no means good at creating <span class="bold">strong</span> passwords. </p>
|
||||
<ol>
|
||||
<li>If you already know that my password creation method is simply <code>sha1(password+website)</code>, all you have to do is try a dictionary attack (or any attack you want on the 'password' bit, really) and apply this algorithm to each attempt until you find the right password. This will be virtually as fast as a basic dictionary attack because hashing is very fast. </li>
|
||||
<li>Assuming you didn't already know I'm creating passwords this way, if just one of my passwords got leaked in plaintext you would know by the look of it that it is merely a SHA-1 hash. From that you could probably assume that what's being hashed is not very strong, and you would attempt the attack described above.</li>
|
||||
<li>A hash has only a very limited set of characters (0-9, a-f), which makes it easier to brute force when used as a password since there are only 16 possibilities for each character (although I'm not sure this is realistic given there are 16^40 ≈ 10^48 possibilities here).</li>
|
||||
<li>The output cannot be used on websites which enforce the usual password creation rules (an uppercase and a lowercase letter, a number, a special character and an obsolete kana like <a target="_blank" href="https://en.wikipedia.org/wiki/Wi_(kana)">ゐ</a> or <a target="_blank" href="https://en.wikipedia.org/wiki/We_(kana)">ゑ</a>).</li>
|
||||
<li>While using SHA-1 is not technically a problem because we don't care about collisions here, we'll need a longer output for what's next.</li>
|
||||
</ol>
|
||||
<p>But before proceeding, let's fix the worst mistake of all: <span class="bold">the name</span>. What could be a good name? <br>Oh, I know! Something that is not exactly the name of a way more popular tool that every linux user knows! We'll go with a mix of 'hash' and 'password': <span class="bold">hshwd</span>.</p>
|
||||
<h2 id="make-it-look-random">Make it look random</h2>
|
||||
<p>The problems #3 and #4 can be fixed by using a larger portion of the ASCII range instead of keeping the hash as-is. While we're at it, we'll replace SHA-1 with SHA-256 to address #5.</p>
|
||||
<p>First, let's hash something using SHA-256, say 'a':</p>
|
||||
<p class="monospace">ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb</p>
|
||||
<p>Now, if we split this hash into pairs of characters ['ca', '97', ... 'bb'], we get hex values going from 00 to FF (0-255), which is the full ASCII range. We don't want the full range though (because of the non-printable characters), so we map our values from (0-255) to (33-126), which is the range of ASCII characters frequently used in random password generators (like <a target="_blank" href="https://github.com/keepassxreboot/keepassxc/blob/develop/src/core/PasswordGenerator.cpp">this one</a>), keep only the integer part, and convert the results to their corresponding ASCII character. Here we end up with:</p>
|
||||
<p class="monospace">jXP'j*ej|g2bY-q=]Qx{(N=JdOLQ`w;e</p>
|
||||
<p class="italic">(this is NOT the name of Elon Musk's latest child)</p>
|
||||
<p class="light">A quick example in case my explanation is unclear: the pair 'ca' is 202 in decimal, so to map it from (0-255) to (33-126) we do 202/255*(126-33)+33 and keep only the integer part which is 106, which corresponds to the ASCII character 'j'.</p>
|
||||
<p>Since SHA-256 outputs follow a uniform distribution, an attacker cannot distinguish between passwords generated by this program and randomly generated passwords. This solves problem #2 because the use of this program is not obvious from the look of the password alone (except for its length). This is also great against brute-force attacks because no combination of characters is more likely than another (and now there are (126-33)^32 ≈ 10^63 possibilities, which is better).</p>
|
||||
<p>Problems #3 and #4 are also solved since the set of characters is much bigger and our generated passwords are very likely to comply with all the password rules (except for the obsolete kana one, sorry for lying to you).</p>
|
||||
<p>Now all that's left is solving problem #1. Should be easy... right?</p>
|
||||
<h2 id="make-it-slow">Make it slow</h2>
|
||||
<p>For now, even though we added a couple steps beyond hashing, this is still a very fast algorithm, therefore the attack described in problem #1 is likely to succeed in a very short time.<br />
|
||||
My first instinct was to slow it down by iterating the whole thing 10.000 times. And it kind of worked: the program would run in 0.2 seconds with a Python implementation, which is barely noticeable as a user but devastating when you need to try millions of possibilities. </p>
|
||||
<p>Now I didn't want the program to rely on users having Python installed, so I re-implemented it in Rust to export binaries, and... you guessed it, the thing ran almost instantly – and that's just on my 7 years-old hardware. What if an attacker had better hardware and made a faster implementation? It became obvious that iterating the program 10.000 times was not the solution.</p>
|
||||
<h2 id="make-it-unbreakable">Make it unbreakable (like Kimmy Schmidt)</h2>
|
||||
<p>If instead of fixing the number of iterations we let the user choose, then even given your base password and attacker wouldn't know how many times the program needs to be looped. They would need to try an <span class="bold">unknown</span> number of iterations and try each possibility against whatever they are trying to break. Now imagine they don't know your password and they need to do that for each password attempt... now that sounds exciting!</p>
|
||||
<p>But we can go even further by adding a 3rd secret (the first two being the password and the number of iterations).</p>
|
||||
<p>If we <a target="_blank" href="https://en.wikipedia.org/wiki/Salt_(cryptography)">salt</a> each iteration like so (let's call hshwd() the function that maps the hash to ASCII characters):</p>
|
||||
<p class="monospace">for i in range(NUMBER_OF_ITERATIONS):<br>
|
||||
  password = hshwd(sha256(SALT+password))</p>
|
||||
<p>Then an attacker would need to try un unknown number of passwords, iterate the program an unknown number of times, each time adding an unknown salt. </p>
|
||||
<h2 id="usability-concerns">Usability concerns</h2>
|
||||
<p>The way I used pwgen was that I assigned it to a keyboard shortcut, and after launching it I'd just have to type my password+website combination and hit enter to copy it to my clipboard. </p>
|
||||
<p>Now that we have added iterations and salt, our options are:</p>
|
||||
<ol>
|
||||
<li>Typing those everytime before typing the password+website combination</li>
|
||||
<li>Typing those once, then storing them in a file and using those settings all subsequent times</li>
|
||||
<li>Cloning the hshwd repo, hardcoding your own values and compiling your own binary for everyday use</li>
|
||||
</ol>
|
||||
<p>Options #2 and #3 are obviously less secure because one would need to make sure their binary or files cannot be accessed by an adversary. </p>
|
||||
<p>Expanding on option #2, the file containing the values could be encrypted using a 'master password', making this whole program a password manager but the passwords are stored in your brain and you still have to type them... which is kinda stupid, but I said it first so I have the copyright on the idea now.</p>
|
||||
<p>For now, I'll go with the lazy option: hardcoding my own values and having my own binary offline on hardware I trust, and having an option #1 available online (see <a href="#downloads">downloads section</a>) for hardware that I use occasionally and/or that is at risk of being stolen/taken.</p>
|
||||
<h2 id="faq">FAQ</h2>
|
||||
<p>(nobody asked me anything) (I'll add real questions if I get any)</p>
|
||||
<h3>Why use SHA-256 instead of a slower hashing function?</h3>
|
||||
<p>Because I want to use hshwd on my Android phone, and the only way I know how to do an Android app right now is using Godot Engine, which only has SHA-1 and SHA-256 implemented.</p>
|
||||
<h3>Why not make the password longer?</h3>
|
||||
<ol>
|
||||
<li>Some websites enforce a length limit on passwords, so longer passwords might be rejected (looking at you, <a target="_blank" href="https://www.francetravail.fr/">France Travail</a>)</li>
|
||||
<li>Modern browsers (like Firefox) generally suggest 'strong' passwords and those are only 15-characters long, so I assume 32 characters is more than enough</li>
|
||||
<li>The use case for this program is generating unique passwords for websites. If you need something longer, this is probably not the right tool.</li>
|
||||
</ol>
|
||||
<h3>Is hshwd safe to use?</h3>
|
||||
<p>I think it is! But I might be wrong, so do not hesitate to correct me if you see any mistakes in this blog post.</p>
|
||||
<h3>What does it NOT protect me against?</h3>
|
||||
<p><a target="_blank" href="https://xkcd.com/538/">This</a>. And also if your computer or smartphone is infected with malware, keyloggers, a <a href="https://en.wikipedia.org/wiki/AI_agent">Magic Trojan Horse™</a>, or <a target="_blank" href="https://en.wikipedia.org/wiki/Windows_Recall">a thing that takes screenshots of your desktop every few seconds</a>.</p>
|
||||
<h3>Why does the logo look like this?</h3>
|
||||
<p>I am only following the <a target="_blank" href="https://velvetshark.com/ai-company-logos-that-look-like-buttholes">latest trends</a>!</p>
|
||||
<h2 id="downloads">Downloads</h2>
|
||||
<p>Downloads are available on the <a href="../projects/hshwd/index.html">project page</a>.</p>
|
||||
<p>:^)</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer" style="--size: 2em"></div>
|
||||
<a href="#" class="btt"><img class="bttimg" src="../media/icons/arrow_upward.png"/></a>
|
||||
</main>
|
||||
<footer>
|
||||
<div id="footer_box">
|
||||
<div class="footer_item">
|
||||
<p>Copyright © 2026 Ailyaut</p>
|
||||
</div>
|
||||
<div class="footer_item">
|
||||
<a target="_blank" href="https://digitalbeacon.co/report/ailyaut-com" style="text-decoration: none;">
|
||||
<p style="color: black; background-color: var(--accent); padding: 0.2em 0.6em 0.2em 0.6em ; border-radius: 1em;">
|
||||
0.01g of CO₂/view
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="footer_item" style="justify-content: flex-end; gap: 1.75em;">
|
||||
<a rel="me" href="https://mastodon.online/@ailyaut" target="_blank"><img src="../media/icons/mastodon.png" class="icon" alt="Mastodon" title="Mastodon"/></a>
|
||||
<a href="https://www.youtube.com/@ailyaut" target="_blank"><img src="../media/icons/youtube.png" class="icon" alt="YouTube" title="YouTube"/></a>
|
||||
<a href="https://ailyaut.bandcamp.com/" target="_blank"><img src="../media/icons/bandcamp.png" class="icon" alt="Bandcamp" title="Bandcamp"/></a>
|
||||
<a href="https://codeberg.org/ailyaut" target="_blank"><img src="../media/icons/git.png" class="icon" alt="Git" title="Git"/></a>
|
||||
<a href="../rss.xml"><img src="../media/icons/rss.png" class="icon" alt="RSS feed" title="RSS feed"/></a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
111
blog/md/20260205+hshwd_release.md
Normal file
111
blog/md/20260205+hshwd_release.md
Normal file
@@ -0,0 +1,111 @@
|
||||
title = Release of hshwd
|
||||
timestamp = 1770323400
|
||||
preview = hshwd is an open-source offline tool that generates strong, unique passwords from weak ones in a deterministic way, using the properties of hash functions.
|
||||
thumb = projects/hshwd/thumb.png
|
||||
+++
|
||||
|
||||
Summary: hshwd is an open-source offline tool that generates strong, unique passwords from weak ones in a deterministic way, using the properties of hash functions.
|
||||
|
||||
## Some background
|
||||
Back in 2023, I made a small tool for generating many complex, unique passwords from simple ones. I called it pwgen, but in reality I could've called it SHA-1 because this is really all it did.
|
||||
The intended use was that you'd type your usual password (you know, the one you use everywhere even though you shouldn't) and then you would append something unique (e.g. the name of the website you're creating a password for), and then the program would return the SHA-1 hash of your input and ta-da! You get a long, unique password that you can generate back every time you need it because hashing is deterministic.
|
||||
|
||||
We're now in 2026 and I have just completed an [online course on cryptography](https://www.coursera.org/learn/crypto) after realising that this topic has been fascinating me for years (thank you [Soatok's blog](https://soatok.blog/)!).
|
||||
|
||||
I am now the most knowledgeable I have ever been (and less than I'll be tomorrow), so now is the perfect time to aknowledge that pwgen had problems and to redo it properly*.
|
||||
|
||||
*that is, to the best of my current knowledge
|
||||
|
||||
## The Good, the Bad and the Ugly
|
||||
While pwgen is good at creating many unique passwords, it is by no means good at creating *strong* passwords.
|
||||
|
||||
1. If you already know that my password creation method is simply sha1(password+website), all you have to do is try a dictionary attack (or any attack you want on the 'password' bit, really) and apply this algorithm to each attempt until you find the right password. This will be virtually as fast as a basic dictionary attack because hashing is very fast.
|
||||
2. Assuming you didn't already know I'm creating passwords this way, if just one of my passwords got leaked in plaintext you would know by the look of it that it is merely a SHA-1 hash. From that you could probably assume that what's being hashed is not very strong, and you would attempt the attack described above.
|
||||
3. A hash has only a very limited set of characters (0-9, a-f), which makes it easier to brute force when used as a password since there are only 16 possibilities for each character (although I'm not sure this is realistic given there are 16^40 ≈ 10^48 possibilities here).
|
||||
4. The output cannot be used on websites which enforce the usual password creation rules (an uppercase and a lowercase letter, a number, a special character and an obsolete kana like [ゐ](https://en.wikipedia.org/wiki/Wi_(kana)) or [ゑ](https://en.wikipedia.org/wiki/We_(kana))).
|
||||
5. While using SHA-1 is not technically a problem because we don't care about collisions here, we'll need a longer output for what's next.
|
||||
|
||||
But before proceeding, let's fix the worst mistake of all: **the name**. What could be a good name? Oh, I know! Something that is not exactly the name of a way more popular tool that every linux user knows! We'll go with a mix of 'hash' and 'password': **hshwd**.
|
||||
|
||||
## Make it look random
|
||||
The problems #3 and #4 can be fixed by using a larger portion of the ASCII range instead of keeping the hash as-is. While we're at it, we'll replace SHA-1 with SHA-256 to address #5.
|
||||
|
||||
First, let's hash something using SHA-256, say 'a':
|
||||
```
|
||||
ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb
|
||||
```
|
||||
|
||||
Now, if we split this hash into pairs of characters ['ca', '97', ... 'bb'], we get hex values going from 00 to FF (0-255), which is the full ASCII range. We don't want the full range though (because of the non-printable characters), so we map our values from (0-255) to (33-126), which is the range of ASCII characters frequently used in random password generators (like [this one](https://github.com/keepassxreboot/keepassxc/blob/develop/src/core/PasswordGenerator.cpp)), keep only the integer part, and convert the results to their corresponding ASCII character. Here we end up with:
|
||||
```
|
||||
jXP'j*ej|g2bY-q=]Qx{(N=JdOLQ`w;e
|
||||
```
|
||||
_(this is NOT the name of Elon Musk's latest child)_
|
||||
|
||||
A quick example in case my explanation is unclear: the pair 'ca' is 202 in decimal, so to map it from (0-255) to (33-126) we do 202/255*(126-33)+33 and keep only the integer part which is 106, which corresponds to the ASCII character 'j'.
|
||||
|
||||
Since SHA-256 outputs follow a uniform distribution, an attacker cannot distinguish between passwords generated by this program and randomly generated passwords. This solves problem #2 because the use of this program is not obvious from the look of the password alone (except for its length). This is also great against brute-force attacks because no combination of characters is more likely than another (and now there are (126-33)^32 ≈ 10^63 possibilities, which is better).
|
||||
|
||||
Problems #3 and #4 are also solved since the set of characters is much bigger and our generated passwords are very likely to comply with all the password rules (except for the obsolete kana one, sorry for lying to you).
|
||||
|
||||
Now all that's left is solving problem #1. Should be easy... right?
|
||||
|
||||
## Make it slow
|
||||
|
||||
For now, even though we added a couple steps beyond hashing, this is still a very fast algorithm, therefore the attack described in problem #1 is likely to succeed in a very short time.
|
||||
My first instinct was to slow it down by iterating the whole thing 10.000 times. And it kind of worked: the program would run in 0.2 seconds with a Python implementation, which is barely noticeable as a user but devastating when you need to try millions of possibilities.
|
||||
|
||||
Now I didn't want the program to rely on users having Python installed, so I re-implemented it in Rust to export binaries, and... you guessed it, the thing ran almost instantly - and that's just on my 7 years-old hardware. What if an attacker had better hardware and made a faster implementation? It became obvious that iterating the program 10.000 times was not the solution.
|
||||
|
||||
## Make it unbreakable (like Kimmy Schmidt)
|
||||
|
||||
If instead of fixing the number of iterations we let the user choose, then even given your base password and attacker wouldn't know how many times the program needs to be looped. They would need to try an **unknown** number of iterations and try each possibility against whatever they are trying to break. Now imagine they don't know your password and they need to do that for each password attempt... now that sounds exciting!
|
||||
|
||||
But we can go even further by adding a 3rd secret (the first two being the password and the number of iterations).
|
||||
|
||||
If we [salt](https://en.wikipedia.org/wiki/Salt_(cryptography)) each iteration like so (let's call hshwd() the function that maps the hash to ASCII characters):
|
||||
```
|
||||
for i in range(NUMBER_OF_ITERATIONS):
|
||||
password = hshwd(sha256(SALT+password))
|
||||
```
|
||||
Then an attacker would need to try un unknown number of passwords, iterate the program an unknown number of times, each time adding an unknown salt.
|
||||
|
||||
## Usability concerns
|
||||
The way I used pwgen was that I assigned it to a keyboard shortcut, and after launching it I'd just have to type my password+website combination and hit enter to copy it to my clipboard.
|
||||
|
||||
Now that we have added iterations and salt, our options are:
|
||||
|
||||
1. Typing those everytime before typing the password+website combination
|
||||
2. Typing those once, then storing them in a file and using those settings all subsequent times
|
||||
3. Cloning the hshwd repo, hardcoding your own values and compiling your own binary for everyday use
|
||||
|
||||
Options #2 and #3 are obviously less secure because one would need to make sure their binary or files cannot be accessed by an adversary.
|
||||
|
||||
Expanding on option #2, the file containing the values could be encrypted using a 'master password', making this whole program a password manager but the passwords are stored in your brain and you still have to type them... which is kinda stupid, but I said it first so I have the copyright on the idea now.
|
||||
|
||||
For now, I'll go with the lazy option: hardcoding my own values and having my own binary offline on hardware I trust, and having an option #1 available online (see downloads section) for hardware that I use occasionally and/or that is at risk of being stolen/taken.
|
||||
|
||||
## FAQ
|
||||
(nobody asked me anything) (I'll add real questions if I get any)
|
||||
|
||||
### Why use SHA-256 instead of a slower hashing function?
|
||||
Because I want to use hshwd on my Android phone, and the only way I know how to do an Android app right now is using Godot Engine, which only has SHA-1 and SHA-256 implemented.
|
||||
|
||||
### Why not make the password longer?
|
||||
|
||||
1. Some websites have a length limit, so longer passwords might be rejected (looking at you, [France Travail](https://www.francetravail.fr/))
|
||||
2. Modern browsers (like Firefox) generally suggest 'strong' passwords and those are only 15-characters long, so I assume 32 characters is more than enough
|
||||
3. The use case for this program is generating unique passwords for websites. If you need something longer, it probably doesn't fall under this use case
|
||||
|
||||
### Is hshwd safe to use?
|
||||
I think it is! But I might be wrong, so do not hesitate to correct me if you see any mistakes in this blog post.
|
||||
|
||||
### What does it NOT protect me against?
|
||||
[This](https://xkcd.com/538/). And also if your computer or smartphone is infected with malware, keyloggers, a [Magic Trojan Horse™](https://en.wikipedia.org/wiki/AI_agent), or [a thing that takes screenshots of your desktop every few seconds](https://en.wikipedia.org/wiki/Windows_Recall).
|
||||
|
||||
### Why does the logo look like this?
|
||||
I am only following the [latest trends](https://velvetshark.com/ai-company-logos-that-look-like-buttholes)!
|
||||
|
||||
## Downloads
|
||||
All the download links are available on the project page.
|
||||
|
||||
:^)
|
||||
34
index.html
34
index.html
@@ -98,6 +98,23 @@
|
||||
<div class="row" style="gap: 2em">
|
||||
<!-- blog -->
|
||||
<div class="column" style="--custom_width: 33%; gap: 1em">
|
||||
<a href="blog/hshwd_release.html" class="card" style="text-decoration: none;">
|
||||
<img src="projects/hshwd/thumb.png
|
||||
" alt="Release of hshwd
|
||||
" title="Release of hshwd
|
||||
" class="card_image"/>
|
||||
<div class="box">
|
||||
<div class="column" style="width: 100%;">
|
||||
<h3 class="card_title">Release of hshwd
|
||||
</h3>
|
||||
<p class="card_text">
|
||||
hshwd is an open-source offline tool that generates strong, unique passwords from weak ones in a deterministic way, using the properties of hash functions.
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div><div class="column" style="--custom_width: 33%; gap: 1em">
|
||||
<a href="blog/gang_release.html" class="card" style="text-decoration: none;">
|
||||
<img src="projects/gang/thumb.png
|
||||
" alt="Release of Gang
|
||||
@@ -127,23 +144,6 @@
|
||||
<p class="card_text">
|
||||
If you’ve been following this blog since its beginnings in 2024, back when the URL was still ailyaut.robotfumeur.fr, then you’ve probably noticed that it hasn’t been updated much in the last year.
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div><div class="column" style="--custom_width: 33%; gap: 1em">
|
||||
<a href="blog/202405_moxxy_progress_report.html" class="card" style="text-decoration: none;">
|
||||
<img src="blog/media/2024/20240407_moxxy_progress.png
|
||||
" alt="Moxxy UI: May 2024 progress report
|
||||
" title="Moxxy UI: May 2024 progress report
|
||||
" class="card_image"/>
|
||||
<div class="box">
|
||||
<div class="column" style="width: 100%;">
|
||||
<h3 class="card_title">Moxxy UI: May 2024 progress report
|
||||
</h3>
|
||||
<p class="card_text">
|
||||
Let's start with a little announcement: from now on, I'll be publishing my .penpot file in the moxxy/design repository on Codeberg on a monthly basis, so that everyone can tinker with the interface to their heart's content! I should have done it from the start, but better late than never!
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
BIN
media/icons/os-android.png
Normal file
BIN
media/icons/os-android.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 295 B |
BIN
media/icons/os-linux.png
Normal file
BIN
media/icons/os-linux.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 429 B |
@@ -197,6 +197,19 @@
|
||||
<div class="spacer" style="--size: 2em;"></div>
|
||||
<h2 id="apps">Apps & Software</h2>
|
||||
<div class="row" style="gap: 2em">
|
||||
<div class="column" style="--custom_width: 33%; gap: 1em">
|
||||
<a href="projects/hshwd/index.html" class="card" style="text-decoration: none;">
|
||||
<img src="projects/hshwd/thumb.png" alt="hshwd" title="hshwd" class="card_image"/>
|
||||
<div class="box">
|
||||
<div class="column" style="width: 100%;">
|
||||
<h3 class="card_title">hshwd <img class="verified" src="media/icons/verified.svg" /></h3>
|
||||
<p class="card_text">
|
||||
A password generator using the properties of hash functions. Successor of pwgen.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="column" style="--custom_width: 33%; gap: 1em">
|
||||
<a href="projects/pwgen/index.html" class="card" style="text-decoration: none;">
|
||||
<img src="projects/pwgen/thumb.png" alt="pwgen" title="pwgen" class="card_image"/>
|
||||
@@ -223,7 +236,6 @@
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="column" style="--custom_width: 33%; gap: 1em"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
141
projects/hshwd/index.html
Normal file
141
projects/hshwd/index.html
Normal file
@@ -0,0 +1,141 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>hshwd – Ailyaut's blog</title>
|
||||
<link rel="icon" type="image/png" href="../../media/icons/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="stylesheet" href="../../style.css" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<meta property="og:title" content="hshwd – Ailyaut's blog" />
|
||||
<meta property="og:type" content="blog" />
|
||||
<meta property="og:url" content="https://ailyaut.com/" />
|
||||
<meta property="og:image" content="https://ailyaut.com/media/preview.png" />
|
||||
<meta property="og:description" content="Hi! I'm Ailyaut, a 25-year-old everything-designer with a particular interest in open source and card games." />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<div id="header_box">
|
||||
<div>
|
||||
<a href="../../index.html" id="header_title">Ailyaut's blog</a>
|
||||
</div>
|
||||
<nav id="header_nav">
|
||||
<a href="../../blog.html" class="nav_button">Blog</a>
|
||||
<a href="../../gallery.html" class="nav_button">Gallery</a>
|
||||
<a href="../../projects.html" class="nav_button_active">Projects</a>
|
||||
<a href="../../about.html" class="nav_button">About</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="box" style="justify-content: space-between; gap: 2em;">
|
||||
<div class="column" style="--custom_width: 60%;">
|
||||
<h1>hshwd</h1>
|
||||
<p>hshwd is an open-source offline tool that generates strong, unique passwords from weak ones in a deterministic way, using the properties of hash functions. It is the successor of <a href="../pwgen/index.html">pwgen</a>.</p>
|
||||
<p>Related blog post: <a href="../../blog/hshwd_release.html">Release of hshwd</a></p>
|
||||
<h2>Table of contents</h2>
|
||||
<p>
|
||||
<a href="#howitworks">How it works</a><br>
|
||||
<a href="#usage">Usage</a><br>
|
||||
<a href="#faq">FAQ</a><br>
|
||||
<a href="#downloads">Downloads</a><br>
|
||||
</p>
|
||||
<h2 id="howitworks">How it works (short version)</h2>
|
||||
<p>The basic principle is very simple: you input an easy-to-remember password, and the app hashes it to make it long and random-looking (in our case, hshwd uses SHA-256). <br>
|
||||
Hashing is very interesting in this context because of the following properties:</p>
|
||||
<ol>
|
||||
<li>Hashing is deterministic: given a same input, you will always obtain the same output (so you can generate back your strong password whenever you need it)</li>
|
||||
<li>Hashing is not reversible: an infinite number of inputs map to the same output, therefore it is impossible to know what the original input was given a single output (so if one of your generated passwords is exposed, it provides no information about the "seed" password you used)</li>
|
||||
<li>The slightest change in the input changes the output completely: this is useful for creating unique passwords that seem unrelated to each other</li>
|
||||
</ol>
|
||||
<p>However, hashing alone is not enough to defend against dictionary attacks because a hash takes negligible time to compute in the case of SHA-256.</p>
|
||||
<p>To defend against this, hshwd iterates the hashing step multiple times to make it slower for an attacker to compute password candidates, and salts each iteration to add yet another unknown variable. The number of iterations and the salt are chosen by the user so that an attacker has no idea how many times they need to iterate the hashing step and what they need to salt each iteration with, which makes it virtually impossible to guess the "seed" password given a generated password.</p>
|
||||
<p>If you want to learn more about the inner workings of hshwd, you can read the <a href="../../blog/hshwd_release.html">release blog post</a> or check the <a target="_blank" href="https://codeberg.org/ailyaut/hshwd_rust">source code</a>.</p>
|
||||
<h2 id="usage">Usage</h2>
|
||||
<p>The intended use case of hshwd is creating many unique passwords for your online accounts.</p>
|
||||
<p>As mentioned in the previous section, to generate a strong password with hshwd you need to choose:</p>
|
||||
<ol>
|
||||
<li>A random number, preferably 5 digits long or more (the larger the number, the slower it is to generate a password)</li>
|
||||
<li>A random salt (anything will do)</li>
|
||||
</ol>
|
||||
<p>Then, type whatever you want as a "seed" password and append a unique string of characters related to the service you're creating a password for (you can, for instance, write the name of the website). This is to ensure that all your passwords are different.</p>
|
||||
<p><span class="bold">Example:</span> <br>
|
||||
Let's create a strong password for our Netflix account, based on the "seed" password '1234' (which is bad, don't do this).<br>
|
||||
With 11.257 iterations and 'salty' as the salt, the input:
|
||||
</p>
|
||||
<p class="monospace">1234+netflix</p>
|
||||
<p>will output the following:</p>
|
||||
<p class="monospace">8/5B9`ec95RNu_EjLCq1mDNE~nsieL"`</p>
|
||||
<p>Let's now create a password for our Gmail account with the same parameters:</p>
|
||||
<p class="monospace">1234+gmail</p>
|
||||
<p>will output the following:</p>
|
||||
<p class="monospace">\I|fo$fB#'8_'VKe;5Z>!N#@Bo{B_lH#</p>
|
||||
<p>A same combination of inputs (number of iterations, salt and password) will always produce the same output, so you don't have to memorize the generated password as you can generate it back every time you need it.</p>
|
||||
<p><span class="bold">Note:</span> It is strongly recommended that you use a strong password as input (not one from <a target="_blank" href="https://en.wikipedia.org/wiki/List_of_the_most_common_passwords">this list</a>, and not one that you have used before).</p>
|
||||
<h2 id="faq">FAQ</h2>
|
||||
<p>See the <a href="../../blog/hshwd_release.html#faq">Release of hshwd</a> blog post.</p>
|
||||
<h2 id="downloads">Downloads</h2>
|
||||
<p>hshwd is open-source and distributed under the MIT License.<br>
|
||||
It is available on desktop as a command line tool (in Rust), and on mobile as an app (made with Godot Engine).
|
||||
</p>
|
||||
|
||||
<div><a target="_blank" href="https://codeberg.org/ailyaut/hshwd_godot/releases/download/v1.0/hshwd-android-debug-arm64-1.0.apk" class="button" style="gap: 0.5em; width: fit-content; margin-bottom: 0.5em;">
|
||||
<img class="icon" src="../../media/icons/os-android.png" />
|
||||
Download for Android<span class="light">ARM64 · 27,8MB</span>
|
||||
</a></div>
|
||||
<p><span class="bold">Checksum: </span>e00d5af04b23d69e4c8bd551ca3bbc311dcaba7b0cfc693dd9468566c97712b1<br>
|
||||
<a target="_blank" href="https://codeberg.org/ailyaut/hshwd_godot">Source code</a>
|
||||
</p>
|
||||
<div class="spacer" style="--size: 0.5em;"></div>
|
||||
<a target="_blank" href="https://codeberg.org/ailyaut/hshwd_rust/releases/download/v1.0/hshwd-linux-x64-cli-1.0" class="button" style="gap: 0.5em; width: fit-content; margin-bottom: 0.5em;">
|
||||
<img class="icon" src="../../media/icons/os-linux.png" />
|
||||
Download for Linux<span class="light">x64 · 1,7MB</span>
|
||||
</a>
|
||||
<p><span class="bold">Checksum: </span>b3b3a7a180d7b19d3be4e085945fb588244ca2dd3f3705cf281792949feabdcc<br>
|
||||
<a target="_blank" href="https://codeberg.org/ailyaut/hshwd_rust">Source code</a>
|
||||
</p>
|
||||
|
||||
<p>Need it on another platform? Both Rust and Godot Engine let you target multiple platforms easily.</p>
|
||||
<p><span class="bold">Additional content: </span><a target="_blank" href="https://codeberg.org/ailyaut/hshwd_python">Python implementation</a></p>
|
||||
|
||||
</div>
|
||||
<div class="column" style="--custom_width: 30%;">
|
||||
<img style="border-radius: 1em;" src="thumb.png"/>
|
||||
<h3 style="margin-bottom: 0.5em;">My role</h3>
|
||||
<p style="margin-bottom: 0;">Programming, UI/UX design</p>
|
||||
<h3>Software used</h3>
|
||||
<p>Rust, Python, Godot Engine, Inkscape</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer" style="--size: 2em"></div>
|
||||
<a href="#" class="btt"><img class="bttimg" src="../../media/icons/arrow_upward.png"/></a>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div id="footer_box">
|
||||
<div class="footer_item">
|
||||
<p>Copyright © 2026 Ailyaut</p>
|
||||
</div>
|
||||
<div class="footer_item">
|
||||
<a target="_blank" href="https://digitalbeacon.co/report/ailyaut-com" style="text-decoration: none;">
|
||||
<p style="color: black; background-color: var(--accent); padding: 0.2em 0.6em 0.2em 0.6em ; border-radius: 1em;">
|
||||
0.01g of CO₂/view
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="footer_item" style="justify-content: flex-end; gap: 1.75em;">
|
||||
<a rel="me" href="https://mastodon.online/@ailyaut" target="_blank"><img src="../../media/icons/mastodon.png" class="icon" alt="Mastodon" title="Mastodon"/></a>
|
||||
<a href="https://www.youtube.com/@ailyaut" target="_blank"><img src="../../media/icons/youtube.png" class="icon" alt="YouTube" title="YouTube"/></a>
|
||||
<a href="https://ailyaut.bandcamp.com/" target="_blank"><img src="../../media/icons/bandcamp.png" class="icon" alt="Bandcamp" title="Bandcamp"/></a>
|
||||
<a href="https://codeberg.org/ailyaut" target="_blank"><img src="../../media/icons/git.png" class="icon" alt="Git" title="Git"/></a>
|
||||
<a href="../../rss.xml"><img src="../../media/icons/rss.png" class="icon" alt="RSS feed" title="RSS feed"/></a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
BIN
projects/hshwd/thumb.png
Normal file
BIN
projects/hshwd/thumb.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
14
rss.xml
14
rss.xml
@@ -11,6 +11,20 @@
|
||||
</image>
|
||||
<atom:link href="https://ailyaut.com/rss.xml" rel="self" type="application/rss+xml" />
|
||||
|
||||
<item>
|
||||
<title>Release of hshwd
|
||||
</title>
|
||||
<link>https://ailyaut.com/blog/hshwd_release.html</link>
|
||||
<description>hshwd is an open-source offline tool that generates strong, unique passwords from weak ones in a deterministic way, using the properties of hash functions.
|
||||
</description>
|
||||
<category>posts</category>
|
||||
|
||||
<guid>https://ailyaut.com/blog/hshwd_release.html</guid>
|
||||
<dc:creator>Ailyaut</dc:creator>
|
||||
<pubDate>Thu, 05 Feb 2026 21:30:00 +0100</pubDate>
|
||||
<image>https://ailyaut.com/projects/hshwd/thumb.png
|
||||
</image>
|
||||
</item>
|
||||
<item>
|
||||
<title>Release of Gang
|
||||
</title>
|
||||
|
||||
@@ -79,7 +79,7 @@ h3 {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
p, ol, ul, li, .bold, .light, .credit, .monospace {
|
||||
p, ol, ul, li, code, .bold, .light, .credit, .monospace {
|
||||
font-family: system-ui, sans-serif;
|
||||
font-weight: 400;
|
||||
font-size: 1em;
|
||||
@@ -109,8 +109,11 @@ a {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.monospace {
|
||||
code, .monospace {
|
||||
font-family: ui-monospace, monospace;
|
||||
}
|
||||
|
||||
.monospace {
|
||||
background-color: var(--surface);
|
||||
border: 1px solid var(--outline);
|
||||
border-radius: 0.5em;
|
||||
@@ -122,16 +125,6 @@ li {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.link_title {
|
||||
text-decoration: underline 4px var(--accent);
|
||||
transition: all 0.2s ease-out;
|
||||
}
|
||||
|
||||
.link_title:hover {
|
||||
margin-right: 0.5em;
|
||||
transition: all 0.2s ease-out;
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
border: none;
|
||||
|
||||
Reference in New Issue
Block a user