how-it-works: explain what 'hi' becomes (AES-GCM scramble before volume)
This commit is contained in:
parent
ae3e4316fb
commit
0ca6b2b31a
1 changed files with 42 additions and 2 deletions
|
|
@ -197,6 +197,46 @@
|
|||
<li><strong>Pubkey</strong> — 1-to-1 ECDH (P-256) between two public
|
||||
keys.</li>
|
||||
</ul>
|
||||
<p>
|
||||
The encryption happens <em>before</em> anything becomes volume, so the carrier
|
||||
never holds the readable text — it holds scrambled bytes. Here is the
|
||||
journey of <code>"hi"</code> in passphrase mode:
|
||||
</p>
|
||||
<ol>
|
||||
<li><strong>Plaintext.</strong> <code>"hi"</code> is two bytes of ASCII:
|
||||
<code>0x68 0x69</code>.</li>
|
||||
<li><strong>AES-256-GCM scrambles it.</strong> A fresh random 12-byte IV
|
||||
(nonce) is generated and the two bytes are encrypted with the
|
||||
passphrase-derived key, producing <code>IV (12)</code> +
|
||||
<code>ciphertext (2)</code> + <code>auth tag (16)</code> =
|
||||
<strong>30 bytes</strong>. The ciphertext is the same length as the plaintext
|
||||
(GCM is a stream mode) and looks like random noise: GCM XORs a key+IV
|
||||
keystream into your bytes, turning <code>0x68 0x69</code> into two
|
||||
unpredictable ones. Because the IV is random every time, sending
|
||||
<code>"hi"</code> twice yields completely different ciphertext, so no
|
||||
patterns leak.</li>
|
||||
<li><strong>Then it is framed, error-corrected, and modulated.</strong> A frame
|
||||
header + CRC wrap the 30 bytes (~43 bytes); each byte becomes a 12-bit
|
||||
Hamming codeword; each codeword becomes Gray-coded N-level volume steps on
|
||||
the 440 Hz carrier.</li>
|
||||
</ol>
|
||||
<div class="diagram">"hi" ─▶ AES-256-GCM ─▶ IV + ciphertext + tag (30 bytes, looks random)
|
||||
─▶ frame + CRC (~43 B) ─▶ Hamming(12,8) codewords ─▶ volume levels</div>
|
||||
<p>
|
||||
By the time it is volume, the readable <code>h</code>,<code>i</code> is gone
|
||||
three layers up. Someone reading the volume locally (the same-UID PulseAudio
|
||||
attack this project documents) demodulates the amplitude back to bytes and gets
|
||||
30 bytes of random-looking ciphertext, not <code>"hi"</code>. Without the
|
||||
passphrase the encryption is irreversible, and the auth tag means tampering
|
||||
fails outright — flip one bit and decryption rejects it.
|
||||
</p>
|
||||
<p>
|
||||
And it is doubled up: that is the <strong>application layer</strong> (AES-GCM).
|
||||
WebRTC independently wraps the whole audio stream in <strong>DTLS-SRTP</strong>,
|
||||
so the carrier is encrypted again in transit. Two separate keys, two separate
|
||||
layers — the readable <code>"hi"</code> exists only in RAM on the two
|
||||
endpoints, never on the wire and never in the volume.
|
||||
</p>
|
||||
<p class="note">
|
||||
Protects against a network observer reading chat content. Does not protect
|
||||
against another process on either endpoint recording the local audio, or an
|
||||
|
|
@ -227,8 +267,8 @@
|
|||
|
||||
<footer style="max-width:820px;margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
|
||||
<span style="color:#777">page integrity</span> · built <span class="stamp-date">2026-05-28</span><br>
|
||||
md5 <span class="stamp-md5">1309e6236d25cd129cb20580b6fc1171</span><br>
|
||||
sha256 <span class="stamp-sha">1561e3dc07925f0c3a92672d401c646ffd5621dcf6665f6d813873a51731a5c6</span><br>
|
||||
md5 <span class="stamp-md5">e4edc61635d2ae846d7bb7438742e8a7</span><br>
|
||||
sha256 <span class="stamp-sha">16e749e33551edad39a1146e3809efc7486b8461eb51d1b1238b70f13301a5b6</span><br>
|
||||
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span>
|
||||
</footer>
|
||||
</body>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue