All my websites are now available at hloth.loki


Today I made all my personal websites under hloth.dev domain available in Lokinet. Just connect, replace .dev with .loki and you'll be presented with a mirror of a clearnet website! Here is this article in Lokinet: http://blog.hloth.loki/blog/now-in-lokinet
While reading about Lokinet and setting it up, I was curious if it's possible to generate a vanity free domain like in the Tor onion network. As it turns out, no one has published this kind of tool before! I only found one repository with Golang program that does not work and seems outdated. I found a broken link to python implementation among issues comments in oxen repositories, but nothing more than that. So I've just decided to build my own vanity generator!

In the past I already published vanity Session ID generator for the Session Instant Messenger so I just had to figure out how loki address is generated. Apparently, it's much harder than that. I started by looking into docs, but typically for oxen products there was no information.
I had to go to C++ source code of Lokinet... Last time I was digging C code of oxen developers I wanted to rip my eyes: I can't wrap my head around passing something into function for it to write inside of it instead of calling it and using the result. It doesn't help that C++ allows you to import all methods/classes from another file just by referencing its name. In JavaScript it's straightforward and you know exactly where function or class definition is, just by looking at top of the file, but in C++ you have to search among all headers files, some of which will be in a separate repository just to make life slightly harder.

Eventually I found service/info.cpp, service/identity.cpp and constants/proto.hpp which are the three files you're looking for, if you want to learn how Lokinet transforms .private files into addresses. The .loki address is basically z-base32 encoded 32-bytes key. The .private file stores this 32-bytes key and a secret seed, from which you can derive the signing key and other keys that we're not interested in, but which are used in lokinet connections.
I wrote a pseudo-language implementation of how it works:
1// 32 bytes2seed = crypto_random_bytes(32)3
4// as of 8th March 20255protoVersion = 06
7// 64 bytes8hash = sha512(seed)9
10// Clamping curve25519: https://neilmadden.blog/2020/05/28/whats-the-curve25519-clamping-all-about/11hash[0] &= 24812hash[31] &= 6313hash[31] |= 6414
15// 32 bytes16privateKey = hash[0:32]17
18// 32 bytes19signingKey = crypto_scalarmult_ed25519_base_noclamp(privateKey)20
21// https://en.wikipedia.org/wiki/Base32#z-base-3222// always 52 characters23addressName = zbase32(signingKey)24
25// always 57 characters26address = addressName + ".loki"27
28// == This part is for writing to .private file: ==29
30// 64 bytes31sValue = concat(seed, signingKey)32
33// https://en.wikipedia.org/wiki/Bencode34serviceInfo = bencode({35 "s": sValue, // Do not convert! just use raw bytes, bencode can handle them36 "v": protoVersion37})38
39// Write serviceInfo as utf-8 string to a .private file
So I implemented that in JS, did a few tests and wow, it worked! Didn't even take me a full day, altough I did get stuck on how encryptionKey is generated (that golang vanity id generator I mentioned was misleading me, I thought that you need to use blake hashing to get the address, but you actually only need the signing key, no encryption key, no nonce, nothing else)
If you want to see a real implementation in JS, here is the final result: https://github.com/VityaSchel/lokinet-vanity-address-generator
I compiled it into binaries so you can just download the CLI and use it, it's very straightforward to generate free vanity domains for Lokinet now. It also runs pretty fast: on my MacBook M1 Pro when running with 8 threads it can go up to 230k generations per second. I gifted my boyfriend MacBook M4 Pro a few weeks ago so we also wanted to test this tool on this monster machine, it completely outperformed by old M1: the new M4 chip can generate 450-500k generations per second. I was able to find a loki domain with 5-character prefix "h1oth" in just a minute or so.
Speaking of which, in case hloth.loki ever expires, you can always use this address to access all my websites:
http://h1oth5jgjqqc1xji8hq1pjgupjzockn56uja7mfsrjcay63oi5ty.loki/
It was fun and all but honestly I need job. Hopefully Session hires me someday. Here is my resume by the way: http://cv.hloth.loki/ 😁