Quick and Dirty ChatGPT Proofreader

While I find ChatGPT’s reliability dubious when it comes to difficult real-life questions, I found one niche where it functions almost flawlessly - proofreading.

For many non-native speakers (or me at least), pinning down all details of English language (especially getting those pesky indefinite articles at correct places) might be difficult. ChatGPT, at least to my untrained eye, seems to do a really nice job when it comes to correcting the output.

And yes, one can use its chat interface directly to do the proofreading, but ChatGPT’s API is reasonably cheap so you might as well make use of it.

var apiEndpoint = "https://api.openai.com/v1/chat/completions";
var apiKey = "sk-XXX";

var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization
    = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", apiKey);

var inputText = File.ReadAllText("<inputfile>");
inputText = "Proofread text below. Output it as markdown.\n\n"
    + inputText.Replace("\r", "");

var requestBody = new {
    model = "gpt-3.5-turbo",
    messages = new[] {
        new {
            role = "user",
            content = inputText,
        }
    }
};

var jsonRequestBody = JsonSerializer.Serialize(requestBody);
var httpContent = new StringContent(jsonRequestBody,
                                    Encoding.UTF8, "application/json");

var httpResponse = await httpClient.PostAsync(apiEndpoint, httpContent);
var responseJson = await httpResponse.Content.ReadAsStringAsync();
dynamic responseObject = JsonSerializer.Deserialize<dynamic>(responseJson);

string outputText = responseObject.GetProperty("choices")[0]
    .GetProperty("message").GetProperty("content").GetString();

Console.WriteLine(outputText);

And yes, this code doesn’t really check for errors and requires a lot more “plumbing” to be a proper application but it does actually work.

Happy proofreading!

Qemu on Ubuntu

For a long time, I used VirtualBox as my virtualization software of choice. Not only did I have experience with it, but it also worked flawlessly, whether on Windows or on Linux. That is until I faced The virtual machine has terminated unexpectedly during startup because of signal 6 error. No matter what, I couldn’t get VirtualBox working on D34010WYK NUC with Ubuntu 22.04 LTS. Well, maybe it was time to revisit my virtualization platform of choice.

My needs were modest. It had to work without a screen (i.e., some form of remote access), it had to support NAT network interface, and it had to support sharing a directory with host (i.e., shared folder functionality).

When I went over all contenders that could work on top of an existing Linux installation, that left me with two — VirtualBox and QEMU. Since I already had issues with VirtualBox, that left me only with QEMU to try.

I remember using QEMU back in the day a lot before I switched to VirtualBox, and it was OK but annoying to set up and it allowed no host directory sharing. Well, things change. Not all — it’s still focused on command-line — but among features, it now had directory sharing.

To install QEMU we need a few prerequisites but actually less than I remember:

apt install -y libvirt-daemon libvirt-daemon-system bridge-utils qemu-system-x86
systemctl enable libvirtd
systemctl start libvirtd
modprobe kvm-intel
adduser root kvm

When it comes to VM setup, you only need to create a disk (I prefer a raw one, but qcow2 and vmdk are also an option):

qemu-img create -f raw disk.raw 20G

After that, you can use qemu-system-x86_64 to boot VM for the first time:

qemu-system-x86_64 -cpu max -smp cpus=4 -m 16G \
  -drive file=disk.raw,format=raw \
  -boot d -cdrom /Temp/alpine-virt-3.17.1-x86_64.iso \
  -vnc :0 --enable-kvm

This will create a VM with 4 CPUs, 16 GB of RAM, and assign it the disk we created before. In addition, it will boot from CDROM containing installation media. To reach its screen we can use VNC on port 5900 (default).

Once installation is done, we power off the VM, and any subsequent boot should omit CDROM:

qemu-system-x86_64 -cpu max -smp cpus=4 -m 16G \
  -drive file=disk.raw,format=raw \
  -virtfs local,path=/shared,mount_tag=sf_shared,security_model=passthrough \
  -vnc :0 --enable-kvm

And that’s all there is to it. As long as our command is running, our VM is too. Of course, if we want to run it in the background, we can do that too by adding the -daemonize parameter.

Unlike with VirtualBox, within the host, we don’t need any extra drivers or guest additions (at least not for Alpine Linux). We just need to mount the disk using 9p file system:

mount -t 9p -o trans=virtio sf_shared /media/sf_shared

If we want to make it permanent, we can add the following line into /etc/fstab:

sf_shared /media/sf_shared 9p trans=virtio,allow_other 0 0

With this, we have an equivalent setup to the one I used VirtualBox for. And all that with less packages installed and definitely with less disk space used.

And yes, I know QEMU is nothing new, and I remember playing with it way before I went with VirtualBox. What changed is support QEMU enjoys within guest VMs and how trouble-free its setup got.

In any case, I’ve solved my problem.

Mikrotik and ED25519 Keys

Well, it seems miracles do happen. According to the 7.9 testing release notes, Mikrotik will finally support ED25519 host keys. But, is this even important? I would argue yes.

First of all, ED25519 keys are MUCH shorter and significantly faster while providing higher security margin than 2048-bit RSA keys. If you want to use the same key to centrally manage your network and you have some underpowered clients, you will definitely feel RSA slowness when establishing connection - especially when dealing with high-ping situations. And shorter keys are not anything to frown upon either as they get much easier to copy/paste than wall of text RSA provides.

Secondly, security of ED25519 seems quite robust and sits somewhere between 2048-bit and 4096-bit RSA key. Unless there is a major breakthrough in cracking ED25519, this is good enough for foreseeable future. When/if quantum computers become a reality, both RSA and ED25519 are fcked so you’re in a losing battle. However, ED25519 keys seem to have a quantum-resistant NTRU-X25519 key exchange in OpenSSH while there is nothing similar for RSA.

Albeit I’m not cryptographer, I do listen to a lot of smart ones and most of them assume any quantum scaling breakthrough necessary to break ED25519 keys will buy a few years at most for RSA algorithm. In short, while both RSA and ED25519 may be doomed at undefined time in the future, it seems unnecessary to avoid faster algorithm ED25519 is today.

Lastly, for me this will mean I can use a single management key once more as, at this time, I’m using ED25519 for most of my needs with RSA being exclusively kept for the purpose of managing Mikrotik. Finally, I’ll be able to use one key to rule them all.

Good luck with upgrade!


[2023-05-04: Unfortunately, ED25519 support is partial at best. If you try to assign key to a user, you’ll get unable to load key file (wrong format or bad passphrase]

[2023-08-18: Well, while ED25519 support has been with us since 7.9, one couldn’t import any ED25519 keys. If 7.12 beta 1 release notes are to be believed (“ssh - added support for user ed25519 public keys”), we should finally have it done fully and properly. Let’s see…]

[2023-11-15: At last, ED25519 is supported by Mikrotik as of RouterOS 7.12]

Hashing It Out

While .NET finally includes CRC-32 and CRC-64 algorithms, it stops at bare minimum and offers only a single standard polynomial for each. Perfectly sufficient if one wants to create something from scratch but woefully inadequate when it comes to integrating with other software.

You see, CRC is just the method of computation and it’s not sufficient to fully describe the result. What you need is polynomial and there’s a bunch of them. At any useful bit length you will find many “standard” polynomials. While .NETs solution gives probably most common 32 and 64 bit variant, it doesn’t cover shorter bit lengths nor does it allow for custom polynomial.

Well, for that purpose I created a library following the same inheritance-from-NonCryptographicHashAlgorithm-class pattern. Not only does it allow for 8, 16, 32, and 64 bit widths, but it also offers a bunch of well-known polynomials in addition to custom polynomial support.

Below is the list of currently supported variants and, as always, code is available on GitHub.

CRC-8CRC-16CRC-32CRC-64
ATMACORNAAL5ECMA-182
AUTOSARARCADCCPGO-ECMA
BLUETOOTHAUG-CCITTAIXMGO-ISO
C2AUTOSARAUTOSARMS
CCITTBUYPASSBASE91-CREDIS
CDMA2000CCITTBASE91-DWE
DARCCCITT-FALSEBZIP2XZ
DVB-S2CCITT-TRUECASTAGNOLI
GSM-ACDMA2000CD-ROM-EDC
GSM-BCMSCKSUM
HITAGDARCDECT-B
I-432-1DDS-110IEEE-802.3
I-CODEDECT-RINTERLAKEN
ITUDECT-XISCSI
LTEDNPISO-HDLC
MAXIMEN-13757JAMCRC
MAXIM-DOWEPCMPEG-2
MIFAREEPC-C1G2PKZIP
MIFARE-MADGENIBUSPOSIX
NRSC-5GSMV-42
OPENSAFETYI-CODEXFER
ROHCIBM-3740XZ
SAE-J1850IBM-SDLC
SMBUSIEC-61158-2
TECH-3250IEEE 802.3
WCDMA2000ISO-HDLD
ISO-IEC-14443-3-A
ISO-IEC-14443-3-B
KERMIT
LHA
LJ1200
LTE
MAXIM
MAXIM-DOW
MCRF4XX
MODBUS
NRSC-5
OPENSAFETY-A
OPENSAFETY-B
PROFIBUS
RIELLO
SPI-FUJITSU
T10-DIF
TELEDISK
TMS37157
UMTS
USB
V-41-LSB
V-41-MSB
VERIFONE
X-25
XMODEM
ZMODEM

Mikrotik Upgrade via SSH

New Mikrotik version came out and my firewall was just a bit too tight to allow remote WinBox connection. But I did have SSH…

And yes, upgrading to new version is easy enough from command line too. It’s just that one needs to execute two (you can omit the check) commands for the same GUI experience.

/system/package/update check-for-updates /system/package/update download /system/reboot

And that’s it, the new version is in.