Duesee's Blog
https://duesee.dev/
Recent content on Duesee's BlogHugo -- gohugo.ioen-usSun, 24 Nov 2024 13:37:57 +0100(Avoid) implementing STARTTLS
https://duesee.dev/p/avoid-implementing-starttls/
Sun, 24 Nov 2024 13:37:57 +0100https://duesee.dev/p/avoid-implementing-starttls/<img src="proxy.php?url=https://duesee.dev/p/avoid-implementing-starttls/banner.svg" alt="Featured image of post (Avoid) implementing STARTTLS" /><h1 id="avoid-implementing-starttls">(Avoid) Implementing STARTTLS
</h1><p>STARTTLS seems simple.
It consists of a single message to switch to encryption.
But as you zoom in, you start to see
<a class="link" href="https://nvd.nist.gov/vuln/detail/CVE-2020-9941" target="_blank" rel="noopener"
>increasingly</a>
<a class="link" href="https://nvd.nist.gov/vuln/detail/CVE-2020-9941" target="_blank" rel="noopener"
>intricate</a>
<a class="link" href="https://nvd.nist.gov/vuln/detail/CVE-2020-15685" target="_blank" rel="noopener"
>issues</a>
<a class="link" href="https://nvd.nist.gov/vuln/detail/CVE-2020-15917" target="_blank" rel="noopener"
>that</a>
<a class="link" href="https://nvd.nist.gov/vuln/detail/CVE-2020-14954" target="_blank" rel="noopener"
>keep</a>
<a class="link" href="https://gitlab.com/muttmua/mutt/-/issues/246#note_366182002" target="_blank" rel="noopener"
>unfolding</a>.
<a class="link" href="https://www.usenix.org/conference/usenixsecurity21/presentation/poddebniak" target="_blank" rel="noopener"
>It’s best to avoid it</a>.</p>
<figure><a href="#ZgotmplZ"><img src="https://duesee.dev/p/avoid-implementing-starttls/Sierpinski_carpet_6.svg" width="400em"></a><figcaption>
<h4>Sierpiński carpet. Infinite perimeter and zero area. Start with a square, split the square into 9 equal squares, remove the central square, and continue recursively.</h4>
</figcaption>
</figure>
<h2 id="yet-someone-needs-it">Yet… someone needs it.
</h2><p>You start bargaining: You know that STARTTLS is a <a class="link" href="https://dl.acm.org/doi/10.1145/2815675.2815695" target="_blank" rel="noopener"
>real-world</a> <a class="link" href="https://www.rfc-editor.org/rfc/rfc7258" target="_blank" rel="noopener"
>attack target</a>.
And you know that it’s an <a class="link" href="https://nostarttls.secvuln.info" target="_blank" rel="noopener"
>easy target</a>.
You <em>really</em> want to avoid it.
You try to make STARTTLS <a class="link" href="https://docs.rs/imap-types/latest/imap_types/#features" target="_blank" rel="noopener"
>unattractive</a>, convince your colleagues to repeat their <a class="link" href="https://archive.fosdem.org/2024/schedule/event/fosdem-2024-2179--protocols-security-of-starttls-in-the-e-mail-context/" target="_blank" rel="noopener"
>talk about STARTTLS at FOSDEM’ 24</a>, submit a follow-up talk to FOSDEM ‘25, too, and <a class="link" href="https://duesee.dev/p/avoid-implementing-starttls" target="_blank" rel="noopener"
>keep repeating that we should avoid it</a></p>
<blockquote>
<p>By the way, the <a class="link" href="https://github.com/modern-email/FOSDEM-25" target="_blank" rel="noopener"
>Call for Participation in the “Modern Email” FOSDEM ‘25 DevRoom</a> is still open!</p>
</blockquote>
<h2 id="and-yet-someone-needs-it">And yet… someone needs it.
</h2><p>As an open-source maintainer in the email space you will (sooner than later) find yourself in a position where someone is desperately asking for help because their email provider doesn’t support implicit TLS but only STARTTLS.
If these servers are operated by smaller organizations, universities, etc., you should ask them to update their infrastructure.
But, sooner than later, you may end up blocked by some unreasonable entity<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. So…</p>
<h1 id="how-to-avoid-implementing-starttls">How to (avoid) implementing STARTTLS?
</h1><blockquote>
<p>… and still making everyone happy?</p>
</blockquote>
<p>STARTTLS consists of two phases: a plaintext and an encrypted phase.
In the plaintext phase, there is always the possibility that data was meddled with by a network attacker.</p>
<p>Consider this trace:</p>
<pre tabindex="0"><code class="language-imap" data-lang="imap">S: * OK Hello, World! // Who is greeting me here, actually?
S: * 100000000000000 exists // Are you serious?
C: A STARTTLS
S: A NO // Uhm... okay?
<----- TLS handshake ----->
C: B fetch 100000000000000 ...
</code></pre><p>Nothing before the TLS handshake can be trusted.
And I mean nothing.
Not the “OK”, not the “Hello, World!”, certainly not the “100000000000000 exists”, and also not the “NO” (which is a typical STARTTLS stripping attack).
We must throw away every byte before the TLS handshake.</p>
<p>This raises the question, “Why should we expose our application to a potential adversary already?” and brings us to my suggestion:
Let’s avoid (most of) STARTTLS.
Let’s not parse anything before the TLS handshake and try to transition to TLS no matter what.
Let’s do what <code>openssl s_client -starttls imap</code> does.</p>
<h2 id="minimal-starttls-implementation-in-rust">Minimal STARTTLS implementation (in Rust)
</h2><p>Our STARTTLS client implementation could be …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Start with plaintext.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">stream</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">stream</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">TcpStream</span>::<span class="n">connect</span><span class="p">(</span><span class="s">"127.0.0.1:1143"</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">stream</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">do_starttls_prefix</span><span class="p">(</span><span class="n">stream</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// We now have a TcpStream that expects a TLS handshake *exactly* as on port 993.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="n">stream</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">};</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Proceed with TLS.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">stream</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">TlsStream</span>::<span class="n">connect</span><span class="p">(</span><span class="n">stream</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Nit: The server won't send another `Greeting` after STARTTLS.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">client</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Client</span>::<span class="n">new_without_greeting</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Continue as you would with implicit TLS (993).
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="c1">//...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="sd">/// Bring a STARTTLS connection to the point where TLS is expected.
</span></span></span><span class="line"><span class="cl"><span class="sd"></span><span class="k">async</span><span class="w"> </span><span class="k">fn</span> <span class="nf">do_starttls_prefix</span><span class="p">(</span><span class="n">stream</span>: <span class="nc">TcpStream</span><span class="p">)</span><span class="w"> </span>-> <span class="nc">TcpStream</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BufReader</span>::<span class="n">new</span><span class="p">(</span><span class="n">stream</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">lines</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">reader</span><span class="p">.</span><span class="n">lines</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Receive greeting.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="c1">// Note: Greeting is *always* a single line.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">lines</span><span class="p">.</span><span class="n">next_line</span><span class="p">().</span><span class="k">await</span><span class="p">.</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Send STARTTLS command.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="n">lines</span><span class="p">.</span><span class="n">get_mut</span><span class="p">().</span><span class="n">write</span><span class="p">(</span><span class="sa">b</span><span class="s">"A STARTTLS</span><span class="se">\r\n</span><span class="s">"</span><span class="p">).</span><span class="k">await</span><span class="p">.</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// Receive (and discard) all lines up until we get a
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="c1">// command completion result for our "A STARTTLS" command.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="k">loop</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">line</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">lines</span><span class="p">.</span><span class="n">next_line</span><span class="p">().</span><span class="k">await</span><span class="p">.</span><span class="n">unwrap</span><span class="p">().</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">line</span><span class="p">.</span><span class="n">starts_with</span><span class="p">(</span><span class="s">"A "</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="k">break</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">lines</span><span class="p">.</span><span class="n">into_inner</span><span class="p">().</span><span class="n">into_inner</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>While this example is specific to IMAP, you can do the same for SMTP, POP3, LDAP, etc.
Think of STARTTLS as an “insecure prefix” that you barely need to overcome to make your server accept your beautiful TLS bytes.
Treat STARTTLS as a guest.
Greet it warmly and offer it what it needs, but keep it from taking over your space so that your routines stay intact.</p>
<h1 id="faq">FAQ
</h1><p><strong>Q: Is the greeting indeed a single line?</strong></p>
<p>Yes. None of the greeting ABNF rules …</p>
<figure><img src="https://duesee.dev/p/avoid-implementing-starttls/greeting.png" width="800em"><figcaption>
<h4>Tree of all ABNF rules used by greeting.</h4>
</figcaption>
</figure>
<p>… allow a newline. Further, “IMAP folklore” suggests that a <code>Code</code> must not contain literals.
At least, all extensions I saw so far seem to be very careful about this “rule”.</p>
<p><strong>Q: Is the loop required?</strong></p>
<p>Yes. IMAP servers may send their <code>* CAPABILITIES ...</code> right after the greeting.
The loop discards these lines.</p>
<p><strong>Q: Why <code>without_greeting</code>?</strong></p>
<p>STARTTLS is defined so that after the transition to TLS, there will not be a second greeting.
This is a minor difference between a session started via implicit TLS (port 993) and a session that we primed towards TLS.</p>
<p><strong>Q: Is <code>.starts_with</code> enough?</strong></p>
<p>Probably yes. The implementation could be made more solid, e.g., by using a random token in case the server sends a literal. But… why should it?</p>
<p><strong>Q: Is good error reporting still possible?</strong></p>
<p>We lose precision in our errors.
When we implement STARTTLS this way, we cannot detect a server that does not advertise STARTTLS or rejects our command.
We will only see a TLS error.
This is the price we pay for a reduced attack surface.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Microsoft supports IMAPS on port 993 but not SUBMISSIONS on port 465. This has an interesting historical background. Today, it’s unreasonable. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
The European Union must keep funding free software
https://duesee.dev/p/the-european-union-must-keep-funding-free-software/
Sat, 20 Jul 2024 15:37:57 +0200https://duesee.dev/p/the-european-union-must-keep-funding-free-software/<img src="proxy.php?url=https://duesee.dev/p/the-european-union-must-keep-funding-free-software/banner.svg" alt="Featured image of post The European Union must keep funding free software" /><h1 id="the-european-union-must-keep-funding-free-software">The European Union must keep funding free software
</h1><p>Initially publishead by <a class="link" href="https://ps.zoethical.org/pub/lettre-publique-aux-ncp-au-sujet-de-ngi/" target="_blank" rel="noopener"
>petites singularités</a>.
English translation provided by <a class="link" href="https://www.ow2.org/view/Events/The_European_Union_must_keep_funding_free_software_open_letter" target="_blank" rel="noopener"
>OW2</a>.</p>
<p>If you want to sign the letter, please publish the letter on your website and complete the table <del>below</del> <a class="link" href="https://pad.public.cat/lettre-NCP-NGI" target="_blank" rel="noopener"
>here</a>.</p>
<h2 id="open-letter-to-the-european-commission">Open Letter to the European Commission
</h2><p>Since 2020, Next Generation Internet (<a class="link" href="https://www.ngi.eu" target="_blank" rel="noopener"
>NGI</a>) programmes, part of European Commission’s Horizon programme, fund free software in Europe using a cascade funding mechanism (see for example NLnet’s <a class="link" href="https://www.nlnet.nl/commonsfund" target="_blank" rel="noopener"
>calls</a>). This year, according to the Horizon Europe working draft detailing funding programmes for 2025, we notice that Next Generation Internet is not mentioned any more as part of Cluster 4.</p>
<p>NGI programmes have shown their strength and importance to supporting the European software infrastructure, as a generic funding instrument to fund digital commons and ensure their long-term sustainability. We find this transformation incomprehensible, moreover when NGI has proven efficient and economical to support free software as a whole, from the smallest to the most established initiatives. This ecosystem diversity backs the strength of European technological innovation, and maintaining the NGI initiative to provide structural support to software projects at the heart of worldwide innovation is key to enforce the sovereignty of a European infrastructure.
Contrary to common perception, technical innovations often originate from European rather than North American programming communities, and are mostly initiated by small-scaled organizations.</p>
<p>Previous Cluster 4 allocated 27 million euros to:</p>
<ul>
<li>“Human centric Internet aligned with values and principles commonly shared in Europe” ;</li>
<li>“A flourishing internet, based on common building blocks created within NGI, that enables better control of our digital life” ;</li>
<li>“A structured ecosystem of talented contributors driving the creation of new internet commons and the evolution of existing internet commons”.</li>
</ul>
<p>In the name of these challenges, more than 500 projects received NGI funding in the first 5 years, backed by 18 organisations managing these European funding consortia.</p>
<p>NGI contributes to a vast ecosystem, as most of its budget is allocated to fund third parties by the means of open calls, to structure commons that cover the whole Internet scope - from hardware to application, operating systems, digital identities or data traffic supervision. This third-party funding is not renewed in the current program, leaving many projects short on resources for research and innovation in Europe.</p>
<p>Moreover, NGI allows exchanges and collaborations across all the Euro zone countries as well as “widening countries” [^1], currently both a success and an ongoing progress, likewise the Erasmus programme before us. NGI also contributes to opening and supporting longer relationships than strict project funding does. It encourages implementing projects funded as pilots, backing collaboration, identification and reuse of common elements across projects, interoperability in identification systems and beyond, and setting up development models that mix diverse scales and types of European funding schemes.</p>
<p>While the USA, China or Russia deploy huge public and private resources to develop software and infrastructure that massively capture private consumer data, the EU can’t afford this renunciation.
Free and open source software, as supported by NGI since 2020, is by design the opposite of potential vectors for foreign interference. It lets us keep our data local and favors a community-wide economy and know-how, while allowing an international collaboration.</p>
<p>This is all the more essential in the current geopolitical context: the challenge of technological sovereignty is central, and free software allows to address it while acting for peace and sovereignty in the digital world as a whole.</p>
About
https://duesee.dev/about/
Tue, 22 Aug 2023 00:00:00 +0000https://duesee.dev/about/<h1 id="hello-world">Hello, World!
</h1><p>I’m Damian Poddebniak, a software engineer and IT security researcher who believes in free software, open access to knowledge, and a world with net-zero greenhouse gas emissions.
I always enjoyed programming but fell in love with (the) Rust (community).
Thus, expect to read a lot about Rust in this blog!
However, I am also human and may write about other things.</p>
<h1 id="freelancing">Freelancing
</h1><p>I recently started a journey as a freelancing software engineer to work on projects and with clients that share my passion for open-source software.</p>
<h2 id="expertise">Expertise
</h2><p>My core strengths encompass email protocols, applied cryptography, public-key infrastructures, and diligent software design in Rust.
Generally, I enjoy writing parsers for complex protocols and designing usable, misuse-resistant APIs.
Still, I am inquisitive and would love to work with geo-information and embedded systems to enter the renewable energy sector.</p>
<p>During my Ph.D. at the University of Applied Sciences in Münster, I co-authored several research papers on end-to-end encrypted email (IMF, MIME, S/MIME, OpenPGP, …), transport encryption (TLS, X.509, …), and web security (ALPN, SOP, …) that uncovered numerous security issues, leading to protocol revisions in Internet standards such as S/MIME, OpenPGP, and the IMAP protocol.</p>
<p>Beyond my technical expertise, I can translate complex technical topics into accessible, engaging content for a broader audience to promote projects effectively.
I’m a transparent person, and, as far as I can tell, people generally enjoy working with me :-)</p>
<h2 id="clients-and-rates">Clients and rates
</h2><p>I am particularly drawn to projects promoting open-source software, secure communication, and clients in the renewable energy sector.
These are my dream clients, as leveraging technology in these spaces is crucial for our future.</p>
<p>My rate is ~800€ / day, and when you choose to work with me, you’re not just hiring a freelancer – you’re investing in a partnership that delivers value beyond the project’s scope.
I am dedicated to ensuring every project concludes with a diligently designed, meticulously documented, easily maintainable software artifact.</p>
<p><strong>You can reach me via <a class="link" href="mailto:[email protected]" >[email protected]</a>. Pronouns: he/him/his.</strong></p>
<h1 id="education">Education
</h1><ul>
<li>
<p><strong>Dr.-Ing.</strong> in Computer Security, 2016-2021
University of Applied Sciences Münster, Münster, DE</p>
</li>
<li>
<p><strong>M.Sc.</strong> in Computer Science, 2014-2016
University of Applied Sciences Münster, Münster, DE</p>
</li>
<li>
<p><strong>B.Sc.</strong> in Computer Science, 2011-2014
University of Applied Sciences Münster, Münster, DE</p>
</li>
</ul>
A gentle introduction to IMAP
https://duesee.dev/p/a-gentle-introduction-to-imap/
Thu, 20 Jul 2023 00:00:00 +0000https://duesee.dev/p/a-gentle-introduction-to-imap/<img src="proxy.php?url=https://duesee.dev/p/a-gentle-introduction-to-imap/banner.svg" alt="Featured image of post A gentle introduction to IMAP" /><h1 id="hello-world-again">Hello, World!, again!
</h1><p>When asked for advice on what to do as a fresh Ph.D. student a few months ago, I said confidently: “Create a blog and publish regularly.” <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>
So… two and a half years, a move to Hamburg, an excursion as a cryptography engineer, and a few new hobbies later, here’s the next post!</p>
<p>Joking aside, the last blog post was crucial to me as it paved the way toward a <a class="link" href="https://nlnet.nl/project/imap-codec/" target="_blank" rel="noopener"
>grant from the NLnet Foundation</a>! <a class="link" href="https://nlnet.nl/" target="_blank" rel="noopener"
>NLnet</a> heavily contributes to an open information society and helps to fix the Internet. I recommend looking at <a class="link" href="https://nlnet.nl/foundation/can_do.html#helpout" target="_blank" rel="noopener"
>what NLnet can do for you</a> and thinking about <a class="link" href="https://nlnet.nl/donating/" target="_blank" rel="noopener"
>what you can do for NLnet</a>!</p>
<p>What motivated this blog post is that I’m just about to conclude the <a class="link" href="https://github.com/duesee/imap-codec" target="_blank" rel="noopener"
>imap-codec</a> project.
And what is the best way to make people look at it?
Exactly: A blog post about IMAP!</p>
<p>Today, I’ll tell you everything I <em>think</em> is helpful to get up to speed with IMAP.
This blog post will be a “gentle” introduction.
The next one will be about everything that makes IMAP hard to tame.</p>
<p><img src="https://duesee.dev/p/a-gentle-introduction-to-imap/xkcd.png"
width="755"
height="614"
srcset="https://duesee.dev/p/a-gentle-introduction-to-imap/xkcd_hu2cce827c7837853f0f249714482f9207_50768_480x0_resize_box_3.png 480w, https://duesee.dev/p/a-gentle-introduction-to-imap/xkcd_hu2cce827c7837853f0f249714482f9207_50768_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="“It took some extra work to build, but now we’ll be able to use it for all our future projects.” – How to ensure your code is never used. “Let’s not overthink it; If this code is still in use that far in the future, we’ll have bigger problems.” – How to ensure your code lives forever."
class="gallery-image"
data-flex-grow="122"
data-flex-basis="295px"
></p>
<h1 id="introduction">Introduction
</h1><h2 id="state-of-mind">State of Mind
</h2><p>IMAP is <em>a lot</em>. It’s a stateful protocol that doesn’t lend itself to a simple implementation and can be intimidating to someone who is used to HTTP. The syntax is <a class="link" href="https://datatracker.ietf.org/doc/html/rfc3501#autoid-95" target="_blank" rel="noopener"
>branched</a>, <a class="link" href="https://git.meli.delivery/meli/meli/pulls/219" target="_blank" rel="noopener"
>subtle</a>, and has <a class="link" href="https://github.com/duesee/imap-codec/issues/301" target="_blank" rel="noopener"
>various</a> <a class="link" href="https://github.com/duesee/imap-codec/issues/184" target="_blank" rel="noopener"
>issues</a>. It doesn’t help either that there are <a class="link" href="https://www.iana.org/assignments/imap-capabilities/imap-capabilities.xhtml" target="_blank" rel="noopener"
>more than 70 extensions</a>, with any of them potentially breaking your understanding of how IMAP operates. Take your time.</p>
<p>It may seem pretentious to use complicated phrases for simple concepts – I sometimes also joke about the term “string interpolation.”
But phrases like “command continuation request,” “command completion result,” “non-synchronizing literal,” etc. provide clarity.</p>
<p>Lastly (and I know this may sound sarcastic): Try to accept things as they are.
Many things are/were there for a reason.
And those that aren’t/weren’t can’t be changed anyway.</p>
<h2 id="piece-of-mind">Piece of Mind
</h2><p>But there is good news!
When using imap-codec, you don’t need to care about syntax.
imap-codec is based on <a class="link" href="https://github.com/duesee/imap-codec/imap-types" target="_blank" rel="noopener"
>imap-types</a>, and it promises that everything you <em>can</em> create <em>is</em> valid.
Try to create something in imap-types! If it works, it’s allowed.</p>
<p>Thus, let’s annotate all messages with the corresponding Rust <code>Debug</code>-print.
As we will see, this makes it obvious what is supported and what is not.</p>
<blockquote>
<p>Maybe the most significant change since the last blog post is that we now have imap-types that position itself as a candidate for a “standard library” for IMAP in Rust. The similarity to <a class="link" href="https://crates.io/crates/http" target="_blank" rel="noopener"
><code>http</code></a> is on purpose :-)</p>
</blockquote>
<h1 id="speaking-imap">Speaking IMAP
</h1><p>The most efficient, most secure, and <a class="link" href="https://datatracker.ietf.org/doc/rfc8314/" target="_blank" rel="noopener"
>recommended</a> way to use IMAP is through TLS on port 993. Non-encrypted connections are traditionally made on port 143 with the option to do an inline transition to TLS.
This is called STARTTLS and <a class="link" href="https://nostarttls.secvuln.info/" target="_blank" rel="noopener"
>should be avoided</a>.</p>
<p>We will use a non-encrypted connection on port 1143 to make our life easier because most operating systems require special permissions when handling port numbers below 1024.</p>
<p>To follow along, you can start imap-codec’s <a class="link" href="https://github.com/duesee/imap-codec#tokio-demo" target="_blank" rel="noopener"
>tokio-server</a> demo using …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="nb">cd</span> imap-codec
</span></span><span class="line"><span class="cl">cargo run -p tokio-server -- 127.0.0.1:1143
</span></span></code></pre></div><p>… and connect to it using …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">nc -C 127.0.0.1 <span class="m">1143</span>
</span></span></code></pre></div><p>We use Netcat with the <code>-C</code> parameter so that all newlines (<code>\n</code>) are encoded as “Internet newlines” (<code>\r\n</code>) as required by IMAP.</p>
<blockquote>
<p><strong>Warning:</strong> Don’t use Netcat to connect to anything remotely important, as nothing is encrypted. If you <em>really</em> want to experiment with a real server, you could use …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">openssl s_client -verify_return_error -crlf -brief -connect <host>:<port>
</span></span></code></pre></div><p>… instead and connect through TLS.</p>
</blockquote>
<h2 id="greeting">Greeting
</h2><p>In contrast to, e.g., HTTP, the server sends the first message in IMAP. Thus, you should already see something like …</p>
<pre tabindex="0"><code class="language-imap" data-lang="imap">* OK [CAPABILITY IMAP4REV1 AUTH=PLAIN] IMAP server ready
</code></pre><p>Let’s use a comment for “bytes on the wire,” denote the role, e.g., the client (<code>C: </code>) or server (<code>S: </code>), and add a <code>Debug</code>-print to show the type instance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// S: * OK [CAPABILITY IMAP4REV1 AUTH=PLAIN] IMAP server ready
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Greeting</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">kind</span>: <span class="nb">Ok</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">code</span>: <span class="nb">Some</span><span class="p">(</span><span class="n">Capability</span><span class="p">([</span><span class="n">Imap4Rev1</span><span class="p">,</span><span class="w"> </span><span class="n">Auth</span><span class="p">(</span><span class="n">AuthMechanism</span><span class="p">(</span><span class="n">Plain</span><span class="p">))]</span><span class="o">+</span><span class="p">)),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">text</span>: <span class="nc">Text</span><span class="p">(</span><span class="s">"IMAP server ready"</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>The type already tells us a bit about <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/response/struct.Greeting.html" target="_blank" rel="noopener"
><code>Greeting</code></a>s. Generally, when you encounter a <code>* </code>, this means “untagged.” We have yet to talk about tags, but greetings are never tagged. Thus, there is no <code>tag</code> field.</p>
<p>The information we can extract from the greeting is a <code>kind</code>, (possibly) a <code>code</code>, and a human-readable <code>text</code>. The greeting <code>kind</code> is usually <code>Ok</code> and signals that the server will serve our connection. But it could as well be… well… look into <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/response/enum.GreetingKind.html" target="_blank" rel="noopener"
><code>GreetingKind</code></a> :-)</p>
<p>The <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/response/enum.Code.html" target="_blank" rel="noopener"
><code>Code</code></a> is optional and may carry additional data. Here, it already tells us what <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/response/enum.Capability.html" target="_blank" rel="noopener"
>capabilities</a> the server supports.</p>
<blockquote>
<p>Note the <code>+</code> at the end of the capabilities vector. This signals we have a <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/core/struct.NonEmptyVec.html" target="_blank" rel="noopener"
><code>NonEmptyVec</code></a>.</p>
</blockquote>
<p>If you are like me, you might have wondered how applications should parse <code>IMAP server ready</code>. They don’t. It’s just a human-readable <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/core/struct.Text.html" target="_blank" rel="noopener"
><code>Text</code></a> that could be shown to a user, e.g., in case of an error. Let’s always use <code>...</code> for brevity.</p>
<blockquote>
<p><strong>Note</strong>: You can toy around with your messages using …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">cargo run --example<span class="o">=</span>parse_<span class="o">{</span>greeting,command,response<span class="o">}</span>
</span></span></code></pre></div><p>… that will parse and <code>Debug</code>-print your input.</p>
</blockquote>
<h2 id="capability-agreement-again">Capability agreement (again?)
</h2><p>If the server doesn’t send its capabilities – or the client doesn’t understand “codes in greetings” – it needs to ask for them using the <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/command/enum.CommandBody.html#variant.Capability" target="_blank" rel="noopener"
><code>CAPABILITY</code></a> command. Let’s pretend we didn’t already know the capabilities and ask again:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// C: A1 CAPABILITY
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Command</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">tag</span>: <span class="nc">Tag</span><span class="p">(</span><span class="s">"A1"</span><span class="p">),</span><span class="w"> </span><span class="n">body</span>: <span class="nc">Capability</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// S: * CAPABILITY IMAP4REV1 AUTH=PLAIN
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Data</span><span class="p">(</span><span class="n">Capability</span><span class="p">([</span><span class="n">Imap4Rev1</span><span class="p">,</span><span class="w"> </span><span class="n">Auth</span><span class="p">(</span><span class="n">AuthMechanism</span><span class="p">(</span><span class="n">Plain</span><span class="p">))]</span><span class="o">+</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// S: A1 OK ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Status</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nb">Ok</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">tag</span>: <span class="nb">Some</span><span class="p">(</span><span class="n">Tag</span><span class="p">(</span><span class="s">"A1"</span><span class="p">)),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">code</span>: <span class="nb">None</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">text</span>: <span class="nc">Text</span><span class="p">(</span><span class="s">"..."</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p>Now is a good time to talk about <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/core/struct.Tag.html" target="_blank" rel="noopener"
><code>Tag</code></a>s. Unlike SMTP and POP3, an IMAP client can send multiple commands immediately, and the server can answer them out of order. Thus, we need a mechanism to match the command/response pairs.</p>
<p>In the above trace, the client used <code>A1</code> as the command tag, and the server responded with two responses, <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/response/enum.Data.html" target="_blank" rel="noopener"
><code>Data</code></a> and a <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/response/enum.Status.html" target="_blank" rel="noopener"
><code>Status</code></a>.
Here, the second response reflects the command tag.</p>
<p>Remember what we said about terminology?</p>
<p>Looking closely, the status tag is <em>optional</em>. In fact, status responses are “command completion results” and say something about a command’s result when tagged. Untagged status responses serve as an alternative form of server data and don’t say anything about a command.</p>
<p>Let’s also stress again that the server can return multiple responses. It’s even more complicated: No response is really “bound” to a command and can be emitted by the server for unrelated reasons. The server can also emit responses despite no “in-flight” command.</p>
<p>The following trace could be perfectly valid:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="n">Command</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="c1">// Tagged with A1 // Command
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Data</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="c1">// Server data
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Data</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="c1">// Server data
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Data</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="c1">// Server data
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Status</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="c1">// Untagged // Server data
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Data</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="c1">// Server data
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Status</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="c1">// Tagged with A1 // Command completion result
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// No in-flight command, but we get more server data.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">Data</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="c1">// Server data
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Status</span><span class="p">(</span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="c1">// Untagged // Server data
</span></span></span></code></pre></div><p>It’s helpful to think of an IMAP server as something that can send (almost) any response <em>at any time</em>. You always need to be prepared to receive a response. If you are interested in something particular, e.g., a specific email, you can ask the server for more responses using a command. But again: Asking doesn’t mean you will immediately get it. The server may tell you about other emails before handing you the requested information (so to say).</p>
<blockquote>
<p><strong>Note</strong>: The IMAP standard describes what server data the client can expect to receive before the command completion result. It may receive more than that but should also receive the “REQUIRED untagged response(s).”</p>
<p>Of course, the server MUST NOT send made-up tags. A command completion result without a command to conclude doesn’t make sense (<a class="link" href="https://nostarttls.secvuln.info/" target="_blank" rel="noopener"
>except for interesting attacks</a> :-))</p>
</blockquote>
<h2 id="authentication">Authentication
</h2><p>Now, let’s look at a login sequence. I hope it has become easier to read by now.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// C: A1 LOGIN alice password
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Command</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">tag</span>: <span class="nc">Tag</span><span class="p">(</span><span class="s">"A1"</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">body</span>: <span class="nc">Login</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">username</span>: <span class="nc">Atom</span><span class="p">(</span><span class="n">AtomExt</span><span class="p">(</span><span class="s">"alice"</span><span class="p">)),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">password</span>: <span class="nc">Atom</span><span class="p">(</span><span class="n">AtomExt</span><span class="p">(</span><span class="s">"password"</span><span class="p">)),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// S: A1 OK ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Status</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">tag</span>: <span class="nb">Some</span><span class="p">(</span><span class="n">Tag</span><span class="p">(</span><span class="s">"A1"</span><span class="p">)),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">code</span>: <span class="nb">None</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">text</span>: <span class="nc">Text</span><span class="p">(</span><span class="s">"..."</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p>IMAP has multiple ways to authenticate. There is a <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/command/enum.CommandBody.html#variant.Login" target="_blank" rel="noopener"
><code>LOGIN</code></a> and <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/command/enum.CommandBody.html#variant.Authenticate" target="_blank" rel="noopener"
><code>AUTHENTICATE</code></a> command with similar purposes. The AUTHENTICATE command makes IMAP usable with authentication mechanisms defined by “SASL.” We can almost ignore SASL here. However, the base IMAP protocol requires you to implement the SASL <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/auth/struct.AuthMechanism.html#associatedconstant.PLAIN" target="_blank" rel="noopener"
><code>AUTH=PLAIN</code></a> mechanisms which would look like this …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// C: A1 AUTHENTICATE PLAIN
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Command</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">tag</span>: <span class="nc">Tag</span><span class="p">(</span><span class="s">"A1"</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">body</span>: <span class="nc">Authenticate</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">mechanism</span>: <span class="nc">AuthMechanism</span><span class="p">(</span><span class="n">Plain</span><span class="p">)</span><span class="w"> </span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Where are the username and password, you ask? Let’s figure it out in the next blog post :-)</p>
<blockquote>
<p><strong>Note</strong>: <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/auth/struct.AuthMechanism.html#associatedconstant.LOGIN" target="_blank" rel="noopener"
><code>AUTH=LOGIN</code></a> is a more verbose, less efficient, non-standardized mechanism invented for unknown reasons.</p>
</blockquote>
<blockquote>
<p><del><strong>Note</strong>: If you want to connect to Gmail through IMAP, you <em>need</em> <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/auth/struct.AuthMechanism.html#associatedconstant.XOAUTH2" target="_blank" rel="noopener"
><code>AUTH=XOAUTH2</code></a> or <code>AUTH=OAUTH2</code>. This requires registering an App with Google to create the necessary tokens. It’s not trivial and took me an hour to do using a Python script I downloaded from the Internet.</del></p>
<p><del>Unfortunately, you can’t speak IMAP with Google by any other means. It’s good for security but complicates testing.</del></p>
<p><strong>Update:</strong> As <a class="link" href="https://news.ycombinator.com/item?id=37059308" target="_blank" rel="noopener"
>pointed out by @csb6 and @sir</a>, it’s still possible to authenticate with a password.
First, make sure to <a class="link" href="https://myaccount.google.com/signinoptions/two-step-verification/enroll-welcome" target="_blank" rel="noopener"
>activate 2FA</a>.
(You can use a security key to keep your phone number private.)
Then, in the 2FA settings, scroll down to the bottom and generate an “app password” for email.
You can use this as a usual password for your gmail account.</p>
</blockquote>
<h2 id="folder-selection">Folder selection
</h2><p>We want to read some emails now, but not so fast! We first need to <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/command/enum.CommandBody.html#variant.Select" target="_blank" rel="noopener"
><code>SELECT</code></a> a folder …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// C: A1 SELECT INBOX
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Command</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">tag</span>: <span class="nc">Tag</span><span class="p">(</span><span class="s">"A1"</span><span class="p">),</span><span class="w"> </span><span class="n">body</span>: <span class="nc">Select</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">mailbox</span>: <span class="nc">Inbox</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// S: * 3 EXISTS
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Data</span><span class="p">(</span><span class="n">Exists</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// S: * 3 RECENT
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Data</span><span class="p">(</span><span class="n">Recent</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// S: * OK [UNSEEN 1] ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Status</span><span class="p">(</span><span class="nb">Ok</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">tag</span>: <span class="nb">None</span><span class="p">,</span><span class="w"> </span><span class="n">code</span>: <span class="nb">Some</span><span class="p">(</span><span class="n">Unseen</span><span class="p">(</span><span class="mi">1</span><span class="p">)),</span><span class="w"> </span><span class="n">text</span>: <span class="nc">Text</span><span class="p">(</span><span class="s">"..."</span><span class="p">)</span><span class="w"> </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// S: * OK [UIDNEXT 8] ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Status</span><span class="p">(</span><span class="nb">Ok</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">tag</span>: <span class="nb">None</span><span class="p">,</span><span class="w"> </span><span class="n">code</span>: <span class="nb">Some</span><span class="p">(</span><span class="n">UidNext</span><span class="p">(</span><span class="mi">8</span><span class="p">)),</span><span class="w"> </span><span class="n">text</span>: <span class="nc">Text</span><span class="p">(</span><span class="s">"..."</span><span class="p">)</span><span class="w"> </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// S: A1 OK ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Status</span><span class="p">(</span><span class="nb">Ok</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">tag</span>: <span class="nb">Some</span><span class="p">(</span><span class="n">Tag</span><span class="p">(</span><span class="s">"A1"</span><span class="p">)),</span><span class="w"> </span><span class="n">code</span>: <span class="nb">None</span><span class="p">,</span><span class="w"> </span><span class="n">text</span>: <span class="nc">Text</span><span class="p">(</span><span class="s">"..."</span><span class="p">)</span><span class="w"> </span><span class="p">})</span><span class="w">
</span></span></span></code></pre></div><p>Great, we got a gazillion responses and a positive command completion result. This means we are in the INBOX now.
And we already know how many messages the INBOX holds due to the <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/response/enum.Data.html#variant.Exists" target="_blank" rel="noopener"
><code>Exists</code></a> response.</p>
<h2 id="fetching-of-email-data">Fetching of email data
</h2><p>Let’s now <a class="link" href="https://docs.rs/imap-types/0.10.0/imap_types/command/enum.CommandBody.html#variant.Fetch" target="_blank" rel="noopener"
><code>FETCH</code></a> the first email:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// C: A1 FETCH 1 (BODY[])
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Command</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">tag</span>: <span class="nc">Tag</span><span class="p">(</span><span class="s">"A1"</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">body</span>: <span class="nc">Fetch</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">sequence_set</span>: <span class="nc">SequenceSet</span><span class="p">([</span><span class="n">Single</span><span class="p">(</span><span class="n">Value</span><span class="p">(</span><span class="mi">1</span><span class="p">))]</span><span class="o">+</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">macro_or_item_names</span>: <span class="nc">MessageDataItemNames</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">[</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">BodyExt</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">section</span>: <span class="nb">None</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">partial</span>: <span class="nb">None</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">peek</span>: <span class="nc">false</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">],</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">uid</span>: <span class="nc">false</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Why does one line result in such a complicated type, you ask?</p>
<p><em>Back answer</em>: Did you know that an IMAP server needs to parse and understand the complete structure of every email it holds to serve partial information? Do you know GraphQL? IMAP already had it!</p>
<p>Besides having <a class="link" href="https://www.usenix.org/conference/usenixsecurity23/presentation/ising" target="_blank" rel="noopener"
>surprising consequences on end-to-end encrypted email</a>, partial fetching allows, e.g., to fetch only all subjects in the inbox.
This is really important to write good clients.
You don’t want your email client to download your whole INBOX (and all attachments) before being able to show you a list of “from,” and “subject.”</p>
<p>You can request subjects only.
Or specific headers.
Or a particular MIME part.</p>
<p>Or …</p>
<ul>
<li>1337 bytes</li>
<li>starting at index 42</li>
<li>of the header</li>
<li>of the second MIME part</li>
<li>of the first MIME part</li>
<li>of the first email plus emails from sequence number 2 to the end</li>
<li>without marking them as read</li>
</ul>
<p>… like …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// Tag
</span></span></span><span class="line"><span class="cl"><span class="c1">// | Fetch by UID
</span></span></span><span class="line"><span class="cl"><span class="c1">// | | Which emails?
</span></span></span><span class="line"><span class="cl"><span class="c1">// | | | What exactly?
</span></span></span><span class="line"><span class="cl"><span class="c1">// | | | |
</span></span></span><span class="line"><span class="cl"><span class="c1">// /--\ /-------\ /---\ /----------------------------\
</span></span></span><span class="line"><span class="cl"><span class="c1">// C: ABC1 UID FETCH 1,2:* (BODY.PEEK[1.2.HEADER]<42.1337>)
</span></span></span><span class="line"><span class="cl"><span class="c1">// \-------/ \--------/ \/ \--/
</span></span></span><span class="line"><span class="cl"><span class="c1">// | | | |
</span></span></span><span class="line"><span class="cl"><span class="c1">// | | | Count
</span></span></span><span class="line"><span class="cl"><span class="c1">// | | Start
</span></span></span><span class="line"><span class="cl"><span class="c1">// | Part
</span></span></span><span class="line"><span class="cl"><span class="c1">// Body (w/o marking as read)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Command</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">tag</span>: <span class="nc">Tag</span><span class="p">(</span><span class="s">"ABC1"</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">body</span>: <span class="nc">Fetch</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">sequence_set</span>: <span class="nc">SequenceSet</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">[</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Single</span><span class="p">(</span><span class="n">Value</span><span class="p">(</span><span class="mi">1</span><span class="p">)),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Range</span><span class="p">(</span><span class="n">Value</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span><span class="w"> </span><span class="n">Asterisk</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">]</span><span class="o">+</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">macro_or_item_names</span>: <span class="nc">MessageDataItemNames</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">[</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">BodyExt</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">section</span>: <span class="nb">Some</span><span class="p">(</span><span class="n">Header</span><span class="p">(</span><span class="nb">Some</span><span class="p">(</span><span class="n">Part</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="o">+</span><span class="p">)))),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">partial</span>: <span class="nb">Some</span><span class="p">((</span><span class="mi">42</span><span class="p">,</span><span class="w"> </span><span class="mi">1337</span><span class="p">)),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">peek</span>: <span class="nc">true</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">],</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">uid</span>: <span class="nc">true</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>You get the idea! Hopefully, you are convinced you don’t want to write this by hand or parse it yourself. Have I already told you about imap-c… just kidding :-)</p>
<h1 id="it-doesnt-end-here-">It doesn’t end here …
</h1><p>Well, yes, this blog post does end here. But I will release another one very soon™. In the next one, I will show you everything I avoided mentioning here. We will cover literals, authentication, and why designing a sound IMAP library is hard.</p>
<p>See you soon!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Of course, having a blog as a Ph.D. student is about something other than <em>publishing</em>. It’s about regular writing and feedback. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Type-Driven Development
https://duesee.dev/p/type-driven-development/
Tue, 26 Jan 2021 00:00:00 +0000https://duesee.dev/p/type-driven-development/<img src="proxy.php?url=https://duesee.dev/p/type-driven-development/banner.svg" alt="Featured image of post Type-Driven Development" /><h1 id="hello-world">Hello, World!
</h1><p>I was <a class="link" href="https://twitter.com/SwiftOnSecurity/status/1479598531803590666" target="_blank" rel="noopener"
>recently reminded</a> about <a class="link" href="https://en.wikipedia.org/wiki/Ward_Cunningham" target="_blank" rel="noopener"
>Cunningham’s Law</a> …</p>
<blockquote>
<p>“The best way to get the right answer on the internet is not to ask a question; it’s to post the wrong answer.” <!-- raw HTML omitted --></p>
<p>– Ward Cunningham</p>
</blockquote>
<p>… and it made me think.</p>
<p>During my Ph.D. I used <a class="link" href="https://obsidian.md" target="_blank" rel="noopener"
>Obsidian</a>, an app to write and cross-reference Markdown documents, to sort my thoughts. However, I didn’t share most of it and never discussed it with a broader audience.</p>
<p>So I figured… why don’t I have a blog?</p>
<p>After wrestling with <a class="link" href="https://www.getzola.org/" target="_blank" rel="noopener"
>Zola</a> for some hours and comically failing to configure a theme, I installed <a class="link" href="https://gohugo.io/" target="_blank" rel="noopener"
>Hugo</a>. No worries, Zola. I’ll try a second time in the future! Anyway… this is my blog now. Hosted under a <a class="link" href="https://domains.google/tld/dev/" target="_blank" rel="noopener"
>HSTS preloaded .dev domain</a>. Served by <a class="link" href="https://caddyserver.com/" target="_blank" rel="noopener"
>Caddy</a>. Structured and styled by the pretty <a class="link" href="https://github.com/CaiJimmy/hugo-theme-stack" target="_blank" rel="noopener"
>Stack</a> theme.</p>
<p>In my first post, I will think aloud about some design decisions I made during the development of the imap-codec library.</p>
<p><img src="https://duesee.dev/p/type-driven-development/duty_calls.png"
width="300"
height="330"
srcset="https://duesee.dev/p/type-driven-development/duty_calls_hu9337f8c20fab7104093d8409040899bc_14103_480x0_resize_box_3.png 480w, https://duesee.dev/p/type-driven-development/duty_calls_hu9337f8c20fab7104093d8409040899bc_14103_1024x0_resize_box_3.png 1024w"
loading="lazy"
alt="Are you coming to bed? I can’t. This is important. What? Someone is WRONG on the Internet."
class="gallery-image"
data-flex-grow="90"
data-flex-basis="218px"
></p>
<h1 id="introduction">Introduction
</h1><h2 id="motivation">Motivation
</h2><p><a class="link" href="https://github.com/duesee/imap-codec" target="_blank" rel="noopener"
>imap-codec</a> was born out of frustration. During a <a class="link" href="https://nostarttls.secvuln.info/" target="_blank" rel="noopener"
>research project</a>, I analyzed IMAP clients for certain behavior and had difficulties constructing syntactically valid IMAP messages.
IMAP is… “branched.” Some syntax elements are required in one place and prohibited in another one. I often stared at hard-to-understand IMAP sessions. Sessions, where I was 100% sure that a response was correct and learned that it wasn’t.</p>
<p>I started imap-codec to gradually improve our tooling and to not make the same errors twice. And, somehow, I remained interested in it. But let me first introduce the domain we are working in.</p>
<h2 id="parsing-and-serialization">Parsing and serialization
</h2><p>imap-codec is an IMAP <em>parser</em> and <em>serializer</em>. The task of a parser is to turn a sequence of bytes, an <code>input</code>, into a more useful data structure, an <code>object</code>. The parsed <code>object</code> provides easy access to the encoded information. The task of a serializer is to “flatten” a parsed <code>object</code> into a sequence of bytes again.</p>
<p>More concretely, imap-codec provides two parsers<!-- raw HTML omitted --><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup><!-- raw HTML omitted -->, <a class="link" href="https://docs.rs/imap-codec/latest/imap_codec/parse/command/fn.command.html" target="_blank" rel="noopener"
><code>command</code></a> and <a class="link" href="https://docs.rs/imap-codec/latest/imap_codec/parse/response/fn.response.html" target="_blank" rel="noopener"
><code>response</code></a>, which turn a sequence of bytes (<code>&[u8]</code>), to either a <a class="link" href="https://docs.rs/imap-codec/latest/imap_codec/types/command/struct.Command.html" target="_blank" rel="noopener"
><code>Command</code></a> or a <a class="link" href="https://docs.rs/imap-codec/latest/imap_codec/types/response/enum.Response.html" target="_blank" rel="noopener"
><code>Response</code></a> object. For the serialization part, <code>Command</code> and <code>Response</code> provide an <code>encode</code> method (via the <a class="link" href="https://docs.rs/imap-codec/latest/imap_codec/codec/trait.Encode.html" target="_blank" rel="noopener"
><code>Encode</code> trait</a>), which turns the parsed objects into bytes.</p>
<p>We will focus on the command part only because it makes the post easier to understand (and write.) But all thoughts apply to other parsers and types as well.</p>
<h2 id="bytes-to-command-and-vice-versa-example">Bytes to command and vice versa (example)
</h2><p>When a user inputs the byte sequence …</p>
<pre tabindex="0"><code class="language-imap" data-lang="imap">A123 select "InBoX"\r\n
</code></pre><p>… to the <code>command</code> parser …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span><span class="w"> </span><span class="n">_</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">command</span><span class="p">(</span><span class="sa">b</span><span class="s">"A123 select </span><span class="se">\"</span><span class="s">InBoX</span><span class="se">\"\r\n</span><span class="s">"</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">.</span><span class="n">unwrap</span><span class="p">();</span><span class="w">
</span></span></span></code></pre></div><p>… it returns a …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="n">Command</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">tag</span>: <span class="nc">Tag</span><span class="p">(</span><span class="s">"A123"</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">body</span>: <span class="nc">CommandBody</span>::<span class="n">Select</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">mailbox</span>: <span class="nc">Mailbox</span>::<span class="n">Inbox</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>… object.</p>
<p>When <code>cmd.encode(...)</code> is called, it generates the byte sequence …</p>
<pre tabindex="0"><code class="language-imap" data-lang="imap">A123 SELECT INBOX\r\n
</code></pre><blockquote>
<p>To eliminate a source of confusion: IMAP commands are prefixed by a <em>tag</em>. We used <code>A123</code> but could have also used <code>FooBar1337</code>. Even… <code><script>alert`XSS`</script></code>. (Yes, you can call a JavaScript function with a mere <a class="link" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals" target="_blank" rel="noopener"
>template literal</a>. No, I don’t know why. Yes, this can be useful in attacks, e.g., in the <a class="link" href="https://alpaca-attack.com/ALPACA.pdf" target="_blank" rel="noopener"
>ALPACA Attack</a>.)</p>
</blockquote>
<p>There isn’t really that much to tell about the usage of imap-codec as a parser. You feed it some bytes and get a fully-parsed and verified-to-be-sane object (and a remainder.) No callbacks. No <a class="link" href="https://langsec.org/papers/langsec-cwes-secdev2016.pdf" target="_blank" rel="noopener"
>shotgun parsing</a>. But there is more happening that is not obvious at first glance.</p>
<h1 id="misuse-resistance-correctness-tightness-">Misuse resistance, correctness, tightness, …
</h1><p>The term “misuse resistance” is better-known in cryptography but can be used more broadly. Basically, it means that a developer can’t use your library in the “wrong way.” Using a cryptographic library in “the wrong way” can lead to <a class="link" href="https://tonyarcieri.com/introducing-miscreant-a-multi-language-misuse-resistant-encryption-library#the-problem-nonce-reuse_2" target="_blank" rel="noopener"
>severe security issues</a>. But many things can be misused with varying consequences. For example, a library (or application) <a class="link" href="https://www.youtube.com/watch?v=bnnacleqg6k" target="_blank" rel="noopener"
>can become perplexing</a> when it is easy to misuse. It’s not just about security.</p>
<p>Currently, I <a class="link" href="https://github.com/duesee/imap-codec#features" target="_blank" rel="noopener"
>describe imap-codec</a> as misuse resistant, and mean that you can’t produce a syntactically invalid or inconsistent IMAP message – even when you try really hard. But there’s more to it. The API encodes <em>domain knowledge</em> about IMAP. In a role of an email client/server developer who is not necessarily an IMAP expert, you will (hopefully) have a hard time screwing up.</p>
<blockquote>
<p>But wait. You’re not the one who has commit rights for imap-codec, are you? You’re not supposed to just change the internals of imap-codec. There are people for that! If someone screws up with imap-codec, it is more likely to be me. So… where is my safety net?</p>
</blockquote>
<h2 id="where-is-my-safety-net">Where is my safety net?
</h2><p>Misuse resistance can happen “on different levels.” You can make your API misuse resistant to your consumers but hard to maintain internally. Sure, you can be careful, but I’m not. I don’t want to be! With that attitude… I better have a good strategy to not make stupid mistakes. Thus, I use Rust’s type system as much as possible and cross my fingers that a good public-facing API evolves from that. Concretely, all types in imap-codec, i.e., all <code>struct</code>s and <code>enum</code>s, should be designed, so that inconsistent state is not representable.</p>
<blockquote>
<p>I know I might bend the term “misuse resistance” here. I could use “correct”, or “tight”, instead. But who said that instantiation of types cannot be resistant to misuse?</p>
</blockquote>
<h2 id="leveraging-rusts-type-system">Leveraging Rust’s type system
</h2><p>The above example …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="n">Command</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">tag</span>: <span class="nc">Tag</span><span class="p">(</span><span class="s">"A123"</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">body</span>: <span class="nc">CommandBody</span>::<span class="n">Select</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">mailbox</span>: <span class="nc">Mailbox</span>::<span class="n">Inbox</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>… shows two instances where imap-codec uses Rust’s type system to enforce invariants on the type level.</p>
<p>First, note how the tag is wrapped in a <code>Tag</code> struct and consider what would happen if a <code>Command</code> could be constructed like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">cmd</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Command</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// `&str` instead of `Tag`
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="n">tag</span>: <span class="s">"A123 X"</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">body</span>: <span class="nc">CommandBody</span>::<span class="n">Select</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// `&str` instead of `Mailbox`
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w"> </span><span class="n">mailbox</span>: <span class="s">"InBoX"</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">};</span><span class="w">
</span></span></span></code></pre></div><p>Serialization of <code>cmd</code> would likely produce:</p>
<pre tabindex="0"><code class="language-imap" data-lang="imap">A123 X SELECT "InBoX"\r\n
^
|
error: no such command "X"
</code></pre><p>Obviously, the tag must not contain any whitespace. But <code>&str</code> is not made to enforce that. Thus, imap-codec has a <code>Tag</code>, a newtype wrapper of <code>&str</code> enforcing additional rules. Especially (but not exclusively) that no whitespace is allowed.</p>
<p>Second, note that the string <code>"InBoX"</code> is not contained in the parsed <code>Command</code>. Instead, the mailbox became <code>Mailbox::Inbox</code> which is serialized as <code>INBOX</code> (without the quotes.)</p>
<p>Unlike other folder names, the Inbox in IMAP is not case-sensitive. If the selected mailbox were saved as a string, a programmer would need to remember to check all cases of “inbox”. The infamous …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="k">if</span><span class="w"> </span><span class="s">"inbox"</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">mailbox</span><span class="p">.</span><span class="n">to_lowercase</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>The <code>Mailbox</code> struct unifies all Inboxes to <code>Mailbox::Inbox</code>. Concretely, it is defined as …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">Mailbox</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Inbox</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Other</span><span class="p">(</span><span class="n">MailboxOther</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>… and there is no way in imap-codec to create a <code>Mailbox::Other(...)</code> that <em>semantically</em> denotes the Inbox.</p>
<blockquote>
<p>C’mon, how bad can it be?</p>
</blockquote>
<p>How bad was the <a class="link" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1749957#c5" target="_blank" rel="noopener"
>recent Firefox outage</a>? I don’t know. But mishandling case sensitivity is a real source of bugs. But, more importantly, as domain specialists, we encode our domain knowledge into the type.</p>
<p>imap-codec uses <em>a lot</em> of these constructs. <code>Atom</code>, <code>Tag</code>, <code>Text</code>, … Even <code>NonEmptyVec<T></code> and <code>NonZeroU32</code>!</p>
<h2 id="imap-section-specifiers-example">IMAP section specifiers (example)
</h2><p>The <a class="link" href="https://datatracker.ietf.org/doc/html/rfc3501#section-6.4.5" target="_blank" rel="noopener"
>IMAP RFC</a> defines a “section specifier” syntax to fetch only specific parts of an email. Here are some examples from the IMAP RFC:</p>
<pre tabindex="0"><code>HEADER // Fetch header
TEXT // Fetch text
1 // Fetch part 1
1.HEADER // Fetch header of part 1
1.TEXT // ...
...
5.3.4.HEADER // ...
...
</code></pre><p>How should we design a type, say, <code>Section</code>, for that?</p>
<p>Our first attempt …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Section</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">path</span>: <span class="nb">Vec</span><span class="o"><</span><span class="kt">u32</span><span class="o">></span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">kind</span>: <span class="nb">Option</span><span class="o"><</span><span class="n">Kind</span><span class="o">></span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">enum</span> <span class="nc">Kind</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Header</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Text</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>… should work. All examples can be constructed, e.g., …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="c1">// HEADER
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Section</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">path</span>: <span class="nc">vec</span><span class="o">!</span><span class="p">[],</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">kind</span>: <span class="nb">Some</span><span class="p">(</span><span class="n">Kind</span>::<span class="n">Header</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// 1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Section</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">path</span>: <span class="nc">vec</span><span class="o">!</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">kind</span>: <span class="nb">None</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// 5.3.4.HEADER
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">Section</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">path</span>: <span class="nc">vec</span><span class="o">!</span><span class="p">[</span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="p">],</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">Kind</span>::<span class="n">Header</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// ...
</span></span></span></code></pre></div><p>However, there is a problem. There exists an invalid combination of <code>path</code> and <code>kind</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="n">Section</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">path</span>: <span class="nc">vec</span><span class="o">!</span><span class="p">[],</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">kind</span>: <span class="nb">None</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>When this is <code>encode</code>d, it will likely result in an empty byte sequence. However, this violates the IMAP specification for a section specifier. Now, we can be careful to not let this happen. But we can do better by redesigning the <code>Section</code> type to ensure that this assignment is <em>not representable</em> in the <code>Section</code> type.</p>
<p>We can merge …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Section</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">path</span>: <span class="nb">Vec</span><span class="o"><</span><span class="kt">u32</span><span class="o">></span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">kind</span>: <span class="nb">Option</span><span class="o"><</span><span class="n">Kind</span><span class="o">></span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>… and …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">Kind</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Header</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Text</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>… to a single …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">Section</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Part</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">path</span>: <span class="nc">NonEmptyVec</span><span class="o"><</span><span class="kt">u32</span><span class="o">></span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Header</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">path</span>: <span class="nb">Vec</span><span class="o"><</span><span class="kt">u32</span><span class="o">></span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Text</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">path</span>: <span class="nb">Vec</span><span class="o"><</span><span class="kt">u32</span><span class="o">></span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Now, our <code>Section</code> is either 1) a <code>Part</code>, in which case the <code>path</code> can not be empty, or 2) a <code>Header</code> or <code>Text</code>, in which case the <code>path</code> <em>can</em> be empty.</p>
<p>Later, we also learn from the IMAP RFC that the numeric path <em>must not</em> contain zeros! Let’s fix that, too. Our final <code>Section</code> type then becomes …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">Section</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Part</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">path</span>: <span class="nc">NonEmptyVec</span><span class="o"><</span><span class="n">NonZeroU32</span><span class="o">></span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Header</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">path</span>: <span class="nb">Vec</span><span class="o"><</span><span class="n">NonZeroU32</span><span class="o">></span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Text</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">path</span>: <span class="nb">Vec</span><span class="o"><</span><span class="n">NonZeroU32</span><span class="o">></span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Ideally, all <code>struct</code>’s and <code>enum</code>s in imap-codec should provide this certain type of quality that any arbitrary assignment of fields should <em>make sense</em>. I saw this property called <a class="link" href="https://www.ecorax.net/tightness/" target="_blank" rel="noopener"
>tightness</a> by Pablo Mansanet. A decent term, if you ask me. Quinn Wilton described it as <a class="link" href="https://github.com/QuinnWilton/programming-with-types-talk" target="_blank" rel="noopener"
>Programming with Types</a>. And there is also the term “Type-Driven Development” used (or even introduced?) by the creator of <a class="link" href="https://www.idris-lang.org/" target="_blank" rel="noopener"
>Idris</a>, Edwin Brady. There is even a book titled “Learn Type-Driven Development” by Yawar Amin and Kamon Ayeva. To be honest… I didn’t know about Type-Driven Development before. I thought this is just how you are supposed to write Rust!</p>
<h1 id="tight-types-can-easily-be-generated">Tight types can easily be generated
</h1><p>A nice benefit of tight types is that you can simply <code>[derive(Arbitrary)]</code>. This allows to easily generate crazy – but valid – messages. This is useful for testing: You can <code>encode(...)</code> arbitrarily generated <code>object</code>s into bytes to test the serialization logic. Afterward, you can parse the serialized bytes to test the parsing logic. And you can compare results to test for correctness. Have a look at <a class="link" href="https://github.com/duesee/imap-codec/tree/main/fuzz#structured-fuzzing-with-arbitrary" target="_blank" rel="noopener"
>this beautiful IMAP command</a> and how imap-codec <a class="link" href="https://github.com/duesee/imap-codec/blob/main/fuzz/fuzz_targets/command_to_bytes_and_back.rs#L33" target="_blank" rel="noopener"
>is fuzzed</a>!</p>
<h1 id="conclusion">Conclusion
</h1><p>Thinking about every type might seem like overkill at first. But it is really not <em>too</em> much work and seems to pay off. And more importantly, I am (somewhat) confident that imap-codec is not affected by injection attacks, which may lead to adventurous security issues. (More on that in some later blog post.)</p>
<p>Rust’s type system, especially algebraic data types, is useful for enforcing invariants on the type level. A good library design can use the type system to encode domain knowledge – think about how Inbox is case-insensitive. While mistakes can still happen in the “design phase” – e.g., a software developer misunderstood something and enforced the wrong thing – later modifications to the library are less likely to violate invariants. This programming strategy helped me work on imap-codec, and I hope that new contributors will also benefit from this safety net.</p>
<p>Also, I feel that if you are willing to spend time on a standard, better encode all you learn into the types you use. You will inevitably forget most details, and it is only a question of time that you break something because of that.</p>
<p>While the <em>direction</em> for imap-codec is (somewhat) clear to me, there is still work to do. Currently, imap-codec exposes (too?) many details. I have some thoughts on why this can either be negative or positive for this kind of library. But I haven’t gotten enough feedback and didn’t have the time to think it through.</p>
<h1 id="appendix">Appendix
</h1><h2 id="parser-correctness-and-malleability">Parser correctness and malleability
</h2><p>You may have noted that imap-codec did not “recreate” what it parsed – <code>A123 select "InBoX"</code> vs. <code>A123 SELECT INBOX</code>.
But, ideally, we want that …</p>
<p>$$
\texttt{parse}(\texttt{serialize}(\texttt{object})) = \texttt{object}
$$</p>
<p>… and …</p>
<p>$$
\texttt{serialize}(\texttt{parse}(\texttt{input})) = \texttt{input}
$$</p>
<p>… for any <code>object</code> and any <code>input</code>. (Excluding the error case.)</p>
<p>Or, in other words, we want parsing and serialization to be inverse to each other. In this case, we call a parser/serializer combination <em>correct</em>.</p>
<p>Let’s think for a moment about what this means in practice. First, we see that it is pretty easy to write a correct parser. Lo and behold …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Object</span><span class="p">(</span><span class="o">&</span><span class="p">[</span><span class="kt">u8</span><span class="p">]);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">parse</span><span class="p">(</span><span class="n">input</span>: <span class="kp">&</span><span class="p">[</span><span class="kt">u8</span><span class="p">])</span><span class="w"> </span>-> <span class="nc">Object</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Object</span><span class="p">(</span><span class="n">input</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">serialize</span><span class="p">(</span><span class="n">object</span>: <span class="nc">Object</span><span class="p">)</span><span class="w"> </span>-> <span class="kp">&</span><span class="p">[</span><span class="kt">u8</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">object</span><span class="p">.</span><span class="mi">0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>… voila!</p>
<p>This parser/serializer combination is correct. But it is not useful. We just wrapped the <code>input</code> in an <code>Object</code> newtype to obtain another domain to map our <code>input</code> to. This shows that the definition of <code>Object</code> – or generally the domain we map our input to – is key to usability and correctness.</p>
<h2 id="imap-codec-is-not-correct">imap-codec is not correct
</h2><p>imap-codec’s parsers are not injective because they can map different bytes, <code>input1</code> and <code>input2</code>, to the same <code>object</code>. This means that the parse function is not a bijection, which means that it <em>cannot have</em> an inverse, which means it can not be <em>correct</em>.</p>
<p>We could archive correctness by preserving all information from the <code>input</code> – even the semantically irrelevant information such as the casing of the “select” command. This is maybe required in “format-preserving” parsing but less useful in protocols.</p>
<h2 id="imap-is-malleable-and-there-is-not-much-we-can-do-about-it">IMAP is malleable, and there is not much we can do about it
</h2><p>Arguing with my security hat on, a data format (or protocol syntax specification) should be non-malleable. Non-malleability means that each message has a unique representation. If this is not the case, you must preserve irrelevant information to archive correctness. Thus, correctness is <em>probably</em> not what you should want from a protocol that is malleable.</p>
<blockquote>
<p>Why do you care?</p>
</blockquote>
<p>Because fuzzing! It’s cooler when you have correctness!</p>
<p>I wanted to use fuzz-testing in imap-codec to …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">object</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">lib_fuzzer_magic</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="fm">assert_eq!</span><span class="p">(</span><span class="n">parse</span><span class="p">(</span><span class="n">encode</span><span class="p">(</span><span class="n">object</span><span class="p">)),</span><span class="w"> </span><span class="n">object</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>and</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">input</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">lib_fuzzer_magic</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="fm">assert_eq!</span><span class="p">(</span><span class="n">encode</span><span class="p">(</span><span class="n">parse</span><span class="p">(</span><span class="n">input</span><span class="p">)),</span><span class="w"> </span><span class="n">input</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>… but I needed to cheat!</p>
<p>Instead of checking that imap-codec can recreates what it has parsed – which it can’t because of malleability – I parse the serialized bytes <em>again</em> and compare the parsed objects …</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Rust" data-lang="Rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">input</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">lib_fuzzer_magic</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">let</span><span class="w"> </span><span class="n">object</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">parse</span><span class="p">(</span><span class="n">input</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">let</span><span class="w"> </span><span class="n">output</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">serialize</span><span class="p">(</span><span class="n">object</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">let</span><span class="w"> </span><span class="n">cheat</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">parse</span><span class="p">(</span><span class="n">output</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Does not work ...
</span></span></span><span class="line"><span class="cl"><span class="c1">// assert_eq!(input, output);
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Cheating ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="fm">assert_eq!</span><span class="p">(</span><span class="n">object</span><span class="p">,</span><span class="w"> </span><span class="n">cheat</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>Despite uncovering a few bugs, I am not quite sure <em>what</em> I test here exactly … For example, this test would not catch if I would screw up and always return the same (wrong) <code>object</code> in the <code>parse</code> function.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>A different parser is required in certain message flows. But <code>command</code> and <code>response</code> are the main entry points. <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Archives
https://duesee.dev/archives/
Mon, 25 Jan 2021 00:00:00 +0000https://duesee.dev/archives/Publications
https://duesee.dev/publications/
Mon, 01 Jan 0001 00:00:00 +0000https://duesee.dev/publications/Search
https://duesee.dev/search/
Mon, 01 Jan 0001 00:00:00 +0000https://duesee.dev/search/Software
https://duesee.dev/software/
Mon, 01 Jan 0001 00:00:00 +0000https://duesee.dev/software/Talks
https://duesee.dev/talks/
Mon, 01 Jan 0001 00:00:00 +0000https://duesee.dev/talks/