<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sachit Madaan</title>
    <description>The latest articles on DEV Community by Sachit Madaan (@sachit21).</description>
    <link>https://dev.to/sachit21</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4028015%2F7b9b96a6-6028-4335-8fc7-e572516e64f7.png</url>
      <title>DEV Community: Sachit Madaan</title>
      <link>https://dev.to/sachit21</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sachit21"/>
    <language>en</language>
    <item>
      <title>What I found when I ran a quantum crypto scanner on certbot</title>
      <dc:creator>Sachit Madaan</dc:creator>
      <pubDate>Tue, 14 Jul 2026 03:58:44 +0000</pubDate>
      <link>https://dev.to/sachit21/what-i-found-when-i-ran-a-quantum-crypto-scanner-on-certbot-28bo</link>
      <guid>https://dev.to/sachit21/what-i-found-when-i-ran-a-quantum-crypto-scanner-on-certbot-28bo</guid>
      <description>&lt;p&gt;NIST finalized the first post-quantum cryptography standards in August 2024. RSA, ECC, and ECDH are all broken by Shor's algorithm, and adversaries are already capturing encrypted data today to decrypt later once quantum hardware matures. This is called "harvest now, decrypt later" and it's not theoretical, it's happening now.&lt;/p&gt;

&lt;p&gt;The migration deadline for US federal agencies is 2030. Most companies have no idea where their vulnerable cryptography lives.&lt;/p&gt;

&lt;p&gt;I built &lt;code&gt;pqc-scan&lt;/code&gt; to solve that. It's a CLI and GitHub Action that scans codebases for quantum-vulnerable cryptography and surfaces findings as inline PR annotations. Here's what I learned when I ran it on real code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why AST-based detection and not regex:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The obvious first approach is string matching. You search for "RSA", flag it and ship it. The problem is that crypto APIs look completely different across libraries and languages. In Python alone, RSA key generation looks like this in the cryptography library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;rsa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_private_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;public_exponent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;65537&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But also like this in pycryptodome:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;RSA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the algorithm might not appear directly in the call at all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;algo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RS256&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;algo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Regex catches maybe 20% of real usages. I used tree-sitter to build a proper AST-based scanner that walks the actual parse tree per language. This lets you detect aliased imports, keyword arguments, and indirect algorithm specification. The tool does a shallow constant-propagation pass to catch variable-passed algorithms like the JWT example above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I found on certbot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I ran pqc-scan against certbot, which manages TLS certificates and is a realistic example of a security-critical Python application.&lt;/p&gt;

&lt;p&gt;With tests excluded, it found these in production code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CRITICAL  PQC001 · RSA Key Generation
certbot/src/certbot/crypto_util.py:258
  rsa.generate_private_key(public_exponent=65537, key_size=bits)

CRITICAL  PQC004 · ECDSA Key Generation
certbot/src/certbot/crypto_util.py:268
  ec.generate_private_key(curve=curve(), backend=default_backend())

CRITICAL  PQC002 · RSA Encryption / Padding
certbot/src/certbot/crypto_util.py:365
  PKCS1v15()

HIGH  PQC010 · MD5 Usage
certbot/src/certbot/_internal/account.py:70
  hashlib.md5()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are real, actionable findings in production code that a certbot developer would need to act on before 2030.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The false positive I had to fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The config scanner was flagging this nginx configuration as vulnerable:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SSLProtocol all -SSLv2 -SSLv3&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It saw "SSLv2" in the string and fired. But the &lt;code&gt;-&lt;/code&gt; prefix means the protocol is being explicitly disabled. This config is actually secure, it's saying "use all protocols except SSLv2 and SSLv3."&lt;/p&gt;

&lt;p&gt;A security tool that flags hardened configurations as vulnerable is worse than no tool. It trains developers to ignore warnings. I fixed the scanner to tokenize the directive and skip any protocol token preceded by &lt;code&gt;-&lt;/code&gt; or &lt;code&gt;!&lt;/code&gt;, and added a regression test asserting zero findings on that exact string.&lt;/p&gt;

&lt;p&gt;This is the core challenge of static analysis - being right about what's dangerous, not just what looks dangerous.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;What the tool produces: *&lt;/em&gt;&lt;br&gt;
Outputs rich console findings, SARIF 2.1.0 for inline GitHub PR annotations, and CycloneDX 1.6 CBOM for compliance audits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pqc-scan
pqc-scan scan &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub Action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sachhg/pqc-scan@v0.1.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full source: &lt;a href="https://github.com/sachhg/pqc-scan" rel="noopener noreferrer"&gt;https://github.com/sachhg/pqc-scan&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The tool currently supports Python, JavaScript/TypeScript, Java, and Go. 110 tests. Self-scan produces zero findings.&lt;/p&gt;

&lt;p&gt;If you run it on your codebase and find patterns it misses, open an issue. The detection rules are the part that most needs real-world feedback.&lt;/p&gt;

</description>
      <category>security</category>
      <category>python</category>
      <category>opensource</category>
      <category>github</category>
    </item>
  </channel>
</rss>
